【问题标题】:What is the logic behind this prolog code?这个序言代码背后的逻辑是什么?
【发布时间】:2014-03-11 19:40:39
【问题描述】:

我有一个简短的 Prolog 代码,可以找到给定列表的最后一个元素。

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

但我对这个程序的逻辑有疑问。为什么我们使用last_of([X], X).,我不明白这一点。你能解释一下吗?

【问题讨论】:

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

标签: prolog


【解决方案1】:

您的程序有几个 cmets:

第一,这个名字用词不当。第一个参数是一个列表,第二个是最后一个元素。但你的名字暗示了其他。更好的名字是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).

当我们已经知道XXs 的最后一个元素时,我们可以得出什么结论?我们可以得出结论,一个长一个元素的列表也将具有与最后一个元素完全相同的X!因此:

last([_|Xs], X) :-
   list(Xs, X).

所以有了这个规则,我们可以让列表越来越长。但前提是我们可以从一个案例开始!出于这个原因,你需要list([X],X). 最好先添加这个事实,因为你也会找到目标list(Xs, a) 的答案。

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

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

【讨论】:

  • 实际上,你能解释一下为什么吗?- last([a],a)。给出真实的。 last_of([_|Tail], X) :- last_of(Tail, X), !.最后一个([X],X)。我不明白第二行。
  • 为什么当我们查询 last([a],a).那么 prolog 给出的是真的吗?
  • 因为,正如您的公理所述,last_of([X], X) 对每个 X 都是正确的。
【解决方案2】:

Prolog 在评估时本质上是递归的。当它到达last_of(Tail, X) 它再次检查 last_of(),将 Tail 作为新的第一个参数。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多