在 Prolog 中递归后返回一个值

Posted

技术标签:

【中文标题】在 Prolog 中递归后返回一个值【英文标题】:Returning a value after a recursion in Prolog 【发布时间】:2011-01-10 22:19:53 【问题描述】:

我决定学习一些逻辑编程,但偶然发现了一个问题。 它是在 SWI Prolog 中编程的。

test(A, B, N):-
 nonvar(B),
 B = final,
 true.

test(A, B, N):-
 N > 2,
 test(A, final, N).

test(A, B, N):-
 N1 is N + 1,
 test(N1, B, N1).

这只是一个样本,除了让我发疯之外没有任何实际用途。

问题在于,当代码达到 true 时,它​​开始回溯并回答“true”。但我需要“返回”值 A。我该怎么做?

【问题讨论】:

一个简单的问题:您真正希望代码做什么? 【参考方案1】:

A 没有与规则正文中的任何内容统一。 prolog 的工作方式是通过术语的统一。您不能像在程序语言中那样“返回” A 。例如,当递归结束时,您希望 A 的值是多少?我不知道你的代码在做什么,所以让我举一个我自己的例子。

  accumulate([], A, A).
  accumulate([H|T], A, N) :- A1 is A + H, accumulate(T, A1, N).

  sum([], 0).
  sum(L, N) :- accumulate(L,0,N).

这是一个求和过程,它将对列表中的值求和并“返回 N”,即列表中值的总和。要调用此过程,您可以这样做:

  sum([2, 3, 4], N).

Prolog 会响应:

  N = 9

请注意,随着递归的进行,累加过程使用 A 作为累加器。也就是说,A 保持运行总和,而 N 是它返回的最终答案。在递归期间,N 没有与任何实数值统一。

在递归的最后一步,即当列表为空时,A的值与N统一,实际上返回了N。


让我们做一个跟踪。

 [trace] 4 ?- test(A, B, 0).
   Call: (7) test(_G417, _G418, 0) ? creep//A unifies with _G417 (internal variable name), B with _G418 and N with 0.
   Call: (8) nonvar(_G418) ? creep
   Fail: (8) nonvar(_G418) ? creep
   Redo: (7) test(_G417, _G418, 0) ? creep//Unifies with clause 2, 
^  Call: (8) 0>2 ? creep
^  Fail: (8) 0>2 ? creep
   Redo: (7) test(_G417, _G418, 0) ? creep //Unifies with clause 3
^  Call: (8) _L183 is 0+1 ? creep
^  Exit: (8) 1 is 0+1 ? creep
   Call: (8) test(1, _G418, 1) ? creep //recursive call, unifies with 
   Call: (9) nonvar(_G418) ? creep
   Fail: (9) nonvar(_G418) ? creep
   Redo: (8) test(1, _G418, 1) ? creep
^  Call: (9) 1>2 ? creep
^  Fail: (9) 1>2 ? creep
   Redo: (8) test(1, _G418, 1) ? creep
^  Call: (9) _L195 is 1+1 ? creep
^  Exit: (9) 2 is 1+1 ? creep
   Call: (9) test(2, _G418, 2) ? creep
   Call: (10) nonvar(_G418) ? creep
   Fail: (10) nonvar(_G418) ? creep
   Redo: (9) test(2, _G418, 2) ? creep
^  Call: (10) 2>2 ? creep
^  Fail: (10) 2>2 ? creep
   Redo: (9) test(2, _G418, 2) ? creep
^  Call: (10) _L207 is 2+1 ? creep
^  Exit: (10) 3 is 2+1 ? creep
   Call: (10) test(3, _G418, 3) ? creep
   Call: (11) nonvar(_G418) ? creep
   Fail: (11) nonvar(_G418) ? creep
   Redo: (10) test(3, _G418, 3) ? creep
^  Call: (11) 3>2 ? creep
^  Exit: (11) 3>2 ? creep
   Call: (11) test(3, final, 3) ? creep
   Call: (12) nonvar(final) ? creep
   Exit: (12) nonvar(final) ? creep
   Call: (12) final=final ? creep
   Exit: (12) final=final ? creep
   Call: (12) true ? creep
   Exit: (12) true ? creep
   Exit: (11) test(3, final, 3) ? creep
   Exit: (10) test(3, _G418, 3) ? creep
   Exit: (9) test(2, _G418, 2) ? creep
   Exit: (8) test(1, _G418, 1) ? creep
   Exit: (7) test(_G417, _G418, 0) ? creep

现在,请注意我标记//A unifies with _G417 (internal variable name), B with _G418 and N with 0. 的跟踪点。此时 A 是您的外部变量,_G417 是您的内部 A。如果此调用成功,它最终会执行 prolog 将仅报告外部变量值。在内部,_G417 从未与其他任何东西统一。我认为问题在于理解 Prolog 的统一模型是如何工作的。

【讨论】:

我相信我的问题有点不同。看看我的样本:我问 Prolog test(A, B, 0)。 Prolog 遍历谓词并计数 N1,直到 N1 > 2。当这种情况发生时,谓词递归调用 test(A, final, N)。所以在这一步A是3,N是3,B是最终的。 nonvar(B) 成功,B = final 成功,所以调用 true。在这一步 Prolog 开始回溯并最终返回 true。但我问了A和B,不是真的。我需要 Prolog 返回的值 A。 我不知道如何强制谓词在分配值 A 处终止。我尝试了削减,但没有成功。 文森特是对的。对于噪音较小的调试,请尝试 test(A, B, N):- write(triple(A,B,N)), N1 is N + 1, test(N1, B, N1)。作为最后一个子句。【参考方案2】:

我这里没有我的 prolog 编译器,但你有没有尝试过类似的东西:

test(A, B, N, A):-
 nonvar(B),
 B = final,
 true.

test(A, B, N, Result):-
 N > 2,
 test(A, final, N, Result).

test(A, B, N, Result):-
 N1 is N + 1,
 test(N1, B, N1, Result).

【讨论】:

当调用 true 时,我需要返回变量 A 的值 3。我该如何做到这一点?您上面的代码没有这样做。谢谢,彼得 这样怎么样? (我会在我回家后 2 小时内让这段代码工作) 上面的代码似乎运行良好,我刚刚试了一下。你能给我一个示例输入/输出的例子吗? 看起来您的解决方案有效。我现在得走了,明天我去测试一下。但我很困惑。为什么Prolog只有一个A时返回true,为什么第一个谓词中有两个A时返回值? 在我看来变量 Result 是多余的。

以上是关于在 Prolog 中递归后返回一个值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用append / 3以递归方式在prolog中构建列表?

Prolog 中的返回值

Prolog递归方法

检查元素后返回列表的Prolog谓词

Prolog:递归无法正常工作

使用空格在prolog中编写递归星号函数