了解Prolog的空列表

时间:2016-12-01 09:14:58

标签: list prolog empty-list

我正在阅读Bratko的 Prolog:人工智能编程。我理解列表的最简单方法是将它们可视化为二叉树,这很顺利。但是,我对空列表[]感到困惑。在我看来,它有两个含义。

  1. 当列表或枚举的一部分时,它被视为一个实际的(空的)列表元素(因为它在树的某个地方,它是某个Head的一部分),例如[a, []]
  2. 当它是Tail中唯一的项目时,它不是一个元素,它实际上是 nothing ,例如[a|[]]
  3. 我的问题是我没有看到背后的逻辑2.为什么列表需要将这种可能的“虚无”作为最后的尾巴?仅仅因为树必须是二进制的?还是有其他原因吗? (换句话说,为什么[]被计为1中的元素。但是当它在2中的尾部时它不是?)此外,是否有最终(最右边,最深)的最终结果树的节点不是'没什么'?

3 个答案:

答案 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,通常会调用上一个示例中的ARest

有关使用差异列表实现队列的信息,请参阅this answer

答案 1 :(得分:1)

您的困惑来自于根据特殊的人性化格式列出打印(和阅读)的事实。因此:

[a, b, c, d]

...是.(a, .(b, .(c, .(d, []))))的语法糖。

.谓词代表两个值:存储在列表中的项目和子列表。当[]参数中存在data时,它将作为数据打印。 换句话说,这个:

[[], []]

...是.([], .([], []))的语法糖。 不会打印最后一个[],因为在该上下文中它不需要。它仅用于标记当前列表的结尾。其他[]是存储在主列表中的列表。

  

我理解这一点,但我不明白为什么需要这个最终的空列表。

最后的空列表是一个约定。它可以写成emptynil(如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的其他内置插件所理解的那样。