我正在阅读Bratko的 Prolog:人工智能编程。我理解列表的最简单方法是将它们可视化为二叉树,这很顺利。但是,我对空列表[]
感到困惑。在我看来,它有两个含义。
[a, []]
[a|[]]
我的问题是我没有看到背后的逻辑2.为什么列表需要将这种可能的“虚无”作为最后的尾巴?仅仅因为树必须是二进制的?还是有其他原因吗? (换句话说,为什么[]
被计为1中的元素。但是当它在2中的尾部时它不是?)此外,是否有最终(最右边,最深)的最终结果树的节点不是'没什么'?
答案 0 :(得分:3)
换句话说,为什么[]被计为1中的元素,但是当它在2中的尾部时它不是?
这是两件不同的事情。 Prolog中的列表是(简并的)二叉树,但也非常像带有指针的语言中的单链表,比如C。
在C中,您将拥有一个struct
,其中包含两个成员:值和指向下一个列表元素的指针。重要的是,当指向next的指针指向sentinel时,这就是列表的结尾。
在Prolog中,你有一个带有arity 2的算符:./2
在第一个参数中保存值,在列表中的其余部分:
.(a, Rest)
Prolog中列表的标记是特殊的[]
。这是不是列表,它是空列表!传统上,如果你愿意,它是一个 atom ,或者是一个带有arity 0的仿函数。
在你的问题中:
[a, []]
实际上是.(a, .([], []))
[a|[]]
实际上是.(a, [])
这就是原因:
?- length([a,[]], N).
N = 2.
现在这是一个包含两个元素的列表,第一个元素是a
,第二个元素是空列表[]
。
?- [a|[]] = [a].
true.
这是一个包含单个元素a
的列表。尾部的[]
只会关闭列表。
问题:.([], [])
是什么类型的列表?
此外,是否存在树的最终(最右边,最深)最终节点不是“没有”的情况?
是的,你可以在那里留一个自由变量;然后,你有一个"洞"在列表的末尾,您可以稍后填写。像这样:
?- A = [a, a|Tail], % partial list with two 'a's and the Tail
B = [b,b], % proper list
Tail = B. % the tail of A is now B
A = [a, a, b, b], % we appended A and B without traversing A
Tail = B, B = [b, b].
您也可以制作循环列表,例如,其中包含无限多x
的列表:
?- Xs = [x|Xs].
Xs = [x|Xs].
这有用吗?我不确定。例如,您可以获得一个重复a, b, c
长度为7的列表,如下所示:
?- ABCs = [a,b,c|ABCs], % a list that repeats "a, b, c" forever
length(L, 7), % a proper list of length 7
append(L, _, ABCs). % L is the first 7 elements of ABCs
ABCs = [a, b, c|ABCs],
L = [a, b, c, a, b, c, a].
在R中至少有很多功能" recycle"较短的向量,因此这可能是一个有效的用例。
有关差异列表的讨论,请参阅this answer,通常会调用上一个示例中的A
和Rest
。
有关使用差异列表实现队列的信息,请参阅this answer。
答案 1 :(得分:1)
您的困惑来自于根据特殊的人性化格式列出打印(和阅读)的事实。因此:
[a, b, c, d]
...是.(a, .(b, .(c, .(d, []))))
的语法糖。
.
谓词代表两个值:存储在列表中的项目和子列表。当[]
参数中存在data
时,它将作为数据打印。
换句话说,这个:
[[], []]
...是.([], .([], []))
的语法糖。
不会打印最后一个[]
,因为在该上下文中它不需要。它仅用于标记当前列表的结尾。其他[]
是存储在主列表中的列表。
我理解这一点,但我不明白为什么需要这个最终的空列表。
最后的空列表是一个约定。它可以写成empty
或nil
(如Lisp),但在Prolog中,它由[]
原子表示。
请注意,在prolog中,您可以将子列表部分保留为未实例化,例如:
[a | T]
与:
相同.(a, T)
这些被称为difference lists。
答案 2 :(得分:1)
你对1.和2.的理解是正确的 - 你的意思是“无”,在元素方面。是的,一个空列表里面没有任何东西(即没有元素)。
具有特殊标记值SENTINEL = []
以标记cons-cells链结束的逻辑,如[1,2,3]
= [1,2|[3]]
= [1,2,3|SENTINEL]
= {{1与.(1,.(2,.(3,SENTINEL)))
= .(1,.(2,3))
类似的某些特殊编码相反,类型一致性。我们想要一个cons单元格的第一个字段(或者,在Prolog中,[1,2|3]
有条件的术语的第一个参数)总是被视为“列表元素”,第二个 - - 作为“清单”。这就是为什么.
中的[]
计为列表的元素(因为它显示为[1, []]
的第一个参数 - 有条件的复合词),而.
位于[]
中}}没有(因为它显示为此术语的 2nd 参数)。
是的,树必须是二进制的 - 即用于编码列表的仿函数[1 | []]
是二进制 - 所以我们应该把它放在最终节点的 tail 字段,它会向我们发出信号 实际上是链的最后一个节点?它必须是某种,一致且易于测试。它还必须代表空列表.
。因此,使用空列表的表示来表示列表的空尾是合乎逻辑的。
是的,拥有非[]
最终“尾巴”完全有效,就像在[]
中一样,这是一个完全有效的Prolog 术语 - 它只是不是表达列表[1,2|3]
,正如Prolog的其他内置插件所理解的那样。