我有简短的Prolog代码,可以找到给定列表的最后一个元素。
last_of([_|Tail], X) :- last_of(Tail, X), !.
last_of([X], X).
但是我对这个程序的逻辑有疑问。为什么我们使用last_of([X], X).
,我无法理解这一点。你能解释一下吗?
答案 0 :(得分:4)
您的计划有几条评论:
第1,这个名字用词不当。第一个参数是列表,第二个参数是最后一个元素。但是你的名字不然。更好的名称是list_last/2
或简称last/2
。
第二,裁员是错位的。实际上,通过简单地交换规则并删除剪切,您可以获得更高效,更具声明性的内容:
last([X] , X).
last([_|Xs], X) :- last(Xs,X).
现在回答你的问题。在Prolog中开始编程时,最好先想象 ground 查询。因此,让我们举几个关系应该成功的例子。
?- last([a],a).
为此,事实last([a],a).
就足够了。我们可以将事实概括为last([X],X).
。在那之后,我们可能会考虑一个包含两个元素的列表,事实last([_,X],X).
将涵盖所有长度为2的列表。等等。
last([X],X).
last([_,X],X).
last([_,_,X],X).
...
现在,让我们概括一下这种模式!让我们将较长列表的情况减少到较短的列表。要做到这一点,我假设我已经知道Xs
的最后一个元素:
???? :-
last(Xs, X).
当我们已经知道X
是Xs
的最后一个元素时,我们能得出什么结论?我们可以得出结论,一个元素长的列表也会与最后一个元素具有相同的X
!因此:
last([_|Xs], X) :-
list(Xs, X).
因此,通过此规则,我们可以使列表越来越长。但是只有在有可能从我们开始的情况下!出于这个原因,您需要list([X],X).
最好先添加 这个事实,因为您也会找到目标list(Xs, a)
的答案。
请注意我查看:-
的具体方式。我把它看成是一个从右到左的箭头,意味着一些新的东西:如果右边的目标是真的,那么左边的目标也是如此。
人们经常尝试按照Prolog执行它们的方式来理解Prolog规则。这不是我从右到左,而是从左到右。然而,这种读取对于人类来说非常不直观,因为Prolog使用了一种非常不寻常的方式来执行:一方面它使用统一,这比模式匹配然后回溯要复杂得多。这两种概念都不存在于传统的编程语言中,从而使您感到困惑,而不是他们的帮助。
答案 1 :(得分:1)
Prolog在评估时具有固有的递归性。到达last_of(Tail, X)
时
它再次检查last_of(),并将Tail作为新的第一个参数。
显然这需要某种方法来停止,所以你会得到一个断言,即X始终是仅包含X的列表中的最后一个元素。