这个prolog代码背后的逻辑是什么?

这个prolog代码背后的逻辑是什么?,prolog,Prolog,我有一个简短的Prolog代码,用于查找给定列表的最后一个元素 last_of([_|Tail], X) :- last_of(Tail, X), !. last_of([X], X). 但我有一个关于这个程序逻辑的问题。为什么我们使用([X],X]的最后一个?,我不明白这一点。你能解释一下吗?Prolog在计算时本质上是递归的。当它到达(尾部,X)的最后一个时 它再次检查()的最后一个参数,将Tail作为新的第一个参数 显然,这需要某种方法来停止,因此您可以断言X始终是仅包含X的列表中的最后

我有一个简短的Prolog代码,用于查找给定列表的最后一个元素

last_of([_|Tail], X) :- last_of(Tail, X), !.
last_of([X], X).

但我有一个关于这个程序逻辑的问题。为什么我们使用([X],X]的最后一个?,我不明白这一点。你能解释一下吗?

Prolog在计算时本质上是递归的。当它到达(尾部,X)的最后一个时 它再次检查()的最后一个参数,将Tail作为新的第一个参数


显然,这需要某种方法来停止,因此您可以断言X始终是仅包含X的列表中的最后一个元素。

Prolog在计算时本质上是递归的。当它到达(尾部,X)的最后一个时 它再次检查()的最后一个参数,将Tail作为新的第一个参数


显然,这需要某种方法来停止,因此您可以断言X始终是仅包含X的列表中的最后一个元素。

您的程序中有几个注释:

第一,这个名字用词不当。第一个参数是列表,第二个是最后一个元素。但你的名字暗示了另一种情况。更好的名称应该是
list\u last/2
或干脆
last/2

第二,切口错位。事实上,通过简单地交换规则和删除切割,您可以获得更高效、更具声明性的:

last([X]   , X).
last([_|Xs], X) :- last(Xs,X).
现在谈谈你的问题。在Prolog中开始编程时,最好先想象地面查询。因此,让我们举几个例子说明这种关系应该成功

?- last([a],a).
要做到这一点,一个事实
last([a],a)。
就足够了。我们可以把这个事实推广到
最后一个([X],X)。
。之后,我们可以考虑一个具有两个元素的列表,其中事实“代码>最后([x,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).
因此,有了这个规则,我们可以让列表越来越长。但前提是我们可以从一个案例开始!因此,您需要
列表([X],X)。
最好先添加此事实,因为您也会找到目标
列表(Xs,a)
的答案

请注意我是如何看待
:-
的。我把它理解为一个从右到左的箭头,暗示着一些新的东西:如果右边的目标是真的,那么左边的目标也是真的


通常,人们试图以Prolog执行规则的方式来理解Prolog规则。这不是像我做的那样从右到左,而是从左到右。然而,这种解读对人类来说是非常不直观的,因为Prolog使用了一种非常不寻常的执行方式:一方面,它使用统一,这比模式匹配和回溯要复杂得多。这两个概念在传统编程语言中都不存在,因此让您感到困惑的不仅仅是它们的帮助。

对您的程序有几点意见:

第一,这个名字用词不当。第一个参数是列表,第二个是最后一个元素。但你的名字暗示了另一种情况。更好的名称应该是
list\u last/2
或干脆
last/2

第二,切口错位。事实上,通过简单地交换规则和删除切割,您可以获得更高效、更具声明性的:

last([X]   , X).
last([_|Xs], X) :- last(Xs,X).
现在谈谈你的问题。在Prolog中开始编程时,最好先想象地面查询。因此,让我们举几个例子说明这种关系应该成功

?- last([a],a).
要做到这一点,一个事实
last([a],a)。
就足够了。我们可以把这个事实推广到
最后一个([X],X)。
。之后,我们可以考虑一个具有两个元素的列表,其中事实“代码>最后([x,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).
因此,有了这个规则,我们可以让列表越来越长。但前提是我们可以从一个案例开始!因此,您需要
列表([X],X)。
最好先添加此事实,因为您也会找到目标
列表(Xs,a)
的答案

请注意我是如何看待
:-
的。我把它理解为一个从右到左的箭头,暗示着一些新的东西:如果右边的目标是真的,那么左边的目标也是真的


通常,人们试图以Prolog执行规则的方式来理解Prolog规则。这不是像我做的那样从右到左,而是从左到右。然而,这种解读对人类来说是非常不直观的,因为Prolog使用了一种非常不寻常的执行方式:一方面,它使用统一,这比模式匹配和回溯要复杂得多。这两个概念在传统编程语言中都不存在,因此让您感到困惑,而不是它们的帮助。

在任何语言中,递归实现都需要一个终止案例,它将决定提供一个结果,而不是进行递归调用。@mbrach:但是,在这种情况下,终止案例编码在递归规则中,这对于程序思维来说是非常不寻常的。在任何语言中,递归实现都需要一个终止案例,它将决定提供一个结果,而不是进行递归调用。@mbrach:但是,在这种情况下,终止案例编码在递归规则中,这对于