【问题标题】:Prolog redoes a call and fails for no apparent reasonProlog 重做一个调用并且无缘无故地失败
【发布时间】:2016-09-17 18:25:46
【问题描述】:

我对 Prolog 还很陌生。无论如何,我正在尝试编写一组递归规则,以返回给定字符代码列表中每个单词的平均字符数。我的代码如下。

medellangd(Text,AvgLen) :-
    medellangd(Text,T,1,0,0),
    AvgLen = T.

medellangd([],AvgLen,Space,Words,Chars) :-
    T is (Chars/Words),
    AvgLen = T.
medellangd([A|B],AvgLen,Space,Words,Chars) :-
    succ(Chars,C),
    updatewords(A,Space,Words,W),
    updatespace(A,S),
    medellangd(B,T,S,W,C),
    AvgLen = T.

updatewords(A,1,Words,W) :-
    member(A, [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122])
    -> succ(Words,S),
       W = S
    ;  W = Words.
updatewords(A,0,Words,W) :-
        W = Words.

updatespace(A,S) :-
    member(A,[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122])
    -> S = 0
    ;  S = 1.

由于我不知道的原因,尽管 AvgLen 获得了正确的值,但当我调用 medellangd([68,69],AvgLen) 时,Prolog 会返回 false。当我跟踪这个调用时,虽然每个调用最初在 AvgLen 获取其值之前退出,但如果我在 AvgLen 赋值后输入分号,Prolog 决定重做“(9) updatewords(68, 1, 0, _G2574)”,并且失败.为什么会这样?

【问题讨论】:

  • 我几乎可以确定您的updatewords/4 是选择点的来源。顺便说一句,我不明白你为什么在谓词定义的末尾做所有这些明确的统一......你有理由吗?

标签: prolog logic backtracking unification redo


【解决方案1】:

您的谓词工作正常,为了找到解决方案 prolog 尝试了所有可能的方式,因此在给出答案 AvgLen=2 之后,它会搜索更多可能的解决方案。当 Prolog 试图找到解决方案时,它会构建一个证明树,在其中保留所有可能的方法来证明目标并一一尝试,直到找到所有正确的答案,并且没有其他方法可以证明目标已经离开。这就是调用redo的原因,以尝试更多可能的解决方案。如果您希望谓词是确定性的 您可以在以下位置添加 cut(!):

medellangd(Text,AvgLen) :-
    medellangd(Text,T,1,0,0),
    AvgLen = T,!.

当找到第一个正确答案时,这些将停止并且不会进一步搜索。

一个简单的例子是了解prolog是如何工作的:

simple_example([]).
simple_example([_]).

如果您查询 simple_example(L),则上述谓词成功。其中 L 为空或只有一个元素。 现在,如果您尝试查询 simple_example([])。或 simple_example([1])。在跟踪中你会看到:

[trace]  ?- simple_example([1]).
   Call: (7) simple_example([1]) ? creep
   Exit: (7) simple_example([1]) ? creep
true.

另一方面,如果您以不同的方式编写相同的示例:

  simple_example2(L):- L=[].
  simple_example2(L):- L=[_].

谓词 simple_example2 显然等同于 simple_example,但如果您查询 simple_example2([])。在跟踪中你会看到,因为我们有 [] 匹配两个 L in simple_example2 它会尝试两者,当然只有第一个是正确的:

[trace]  ?- simple_example2([1]).
   Call: (7) simple_example2([1]) ? Unknown option (h for help)
   Call: (7) simple_example2([1]) ? Unknown option (h for help)
   Call: (7) simple_example2([1]) ? Unknown option (h for help)
   Call: (7) simple_example2([1]) ? creep
   Call: (8) [1]=[] ? creep
   Fail: (8) [1]=[] ? creep
   Redo: (7) simple_example2([1]) ? creep
   Call: (8) [1]=[_G3328] ? creep
   Exit: (8) [1]=[1] ? creep
   Exit: (7) simple_example2([1]) ? creep
true.

【讨论】:

  • 是否可以通过算法确保 Prolog 在证明树中只有一条路径?
  • 不,它与算法无关,而是prolog如何理解是否有更多解决方案。这是 prolog 搜索的主要功能,我将更新答案以解释 prolog 如何在一个非常简单的示例中做出选择,但在更复杂的程序中,确保 prolog 没有其他选项是没有意义的。如果您只想要一种解决方案,您可以再次使用 cut。
  • 您知道什么可能导致我的案例失败吗?
  • 应该有第二个解决方案吗?如果没有,如果您编写了一个谓词来解决一个解决方案的问题,那么其他一切(其他搜索解决方案)都应该失败。
  • 我知道我的算法是错误的,因为 medellangd([68,69,91,98,91,91,91,91,97],S) 给出 S = 3。
猜你喜欢
  • 2011-07-02
  • 1970-01-01
  • 2021-06-04
  • 2016-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多