【问题标题】:Why does changing the order of facts change the behavior of the predicate?为什么改变事实的顺序会改变谓词的行为?
【发布时间】:2011-11-09 20:16:22
【问题描述】:

这是我的第一个想法:

perm([X|Y],Z) :- takeout(X,Z,W), perm(Y, W).   
perm([],[]).

当我尝试运行-? perm([1, 2, 3], P). 时,它显示了堆栈溢出问题。

但是如果我们改变这两个语句的顺序,它就会起作用。

perm([X|Y],Z) :- perm(Y, W), takeout(X,Z,W).  
perm([],[]). 

为什么?我是Prolog初学者,请帮忙。

【问题讨论】:

  • 显示takeout列表可能很有用

标签: list prolog permutation dcg


【解决方案1】:

你所指的takeout/3俗称select(X, Xs0, Xs)

这是另一个定义 - 说明 DCG 的不常见用法。

perm(Xs,Ys) :-
   phrase(perm(Xs),[],Ys).

perm([]) --> [].
perm([X|Xs]) --> perm(Xs), ins(X).

ins(X),[X] --> [].
ins(X),[Y] --> [Y], ins(X).

【讨论】:

    【解决方案2】:

    好吧,您的 takeout 谓词可能如下所示:

    takeout( X, [X|R], R ).
    takeout( X, [F|R], [F|S] ) :-
        takeout( X, R, S ).
    

    SWI-Prolog 有一个有用的谓词trace

    第一种情况:

    X = [1, 2, 3] ;
       Redo: (10) takeout(3, _G477, _G485) ? creep
       Call: (11) takeout(3, _G480, _G483) ? creep
       Exit: (11) takeout(3, [3|_G483], _G483) ? creep
       Exit: (10) takeout(3, [_G479, 3|_G483], [_G479|_G483]) ? creep
       Call: (10) perm([], [_G479|_G483]) ? creep
       Fail: (10) perm([], [_G479|_G483]) ? creep
       Redo: (11) takeout(3, _G480, _G483) ? creep
       Call: (12) takeout(3, _G486, _G489) ? creep
       Exit: (12) takeout(3, [3|_G489], _G489) ? creep
       Exit: (11) takeout(3, [_G485, 3|_G489], [_G485|_G489]) ? creep
       Exit: (10) takeout(3, [_G479, _G485, 3|_G489], [_G479, _G485|_G489]) ? creep
       Call: (10) perm([], [_G479, _G485|_G489]) ? creep
       Fail: (10) perm([], [_G479, _G485|_G489]) ? creep
       Redo: (12) takeout(3, _G486, _G489) ? creep
       Call: (13) takeout(3, _G492, _G495) ? creep
       Exit: (13) takeout(3, [3|_G495], _G495) ? creep
       Exit: (12) takeout(3, [_G491, 3|_G495], [_G491|_G495]) ? creep
       Exit: (11) takeout(3, [_G485, _G491, 3|_G495], [_G485, _G491|_G495]) ? creep
       Exit: (10) takeout(3, [_G479, _G485, _G491, 3|_G495], [_G479, _G485, _G491|_G495]) ? creep
       Call: (10) perm([], [_G479, _G485, _G491|_G495]) ? creep
       Fail: (10) perm([], [_G479, _G485, _G491|_G495]) ? creep
       Redo: (13) takeout(3, _G492, _G495) ? creep
       Call: (14) takeout(3, _G498, _G501) ? creep
       Exit: (14) takeout(3, [3|_G501], _G501) ? creep
       Exit: (13) takeout(3, [_G497, 3|_G501], [_G497|_G501]) ? creep
       Exit: (12) takeout(3, [_G491, _G497, 3|_G501], [_G491, _G497|_G501]) ? creep
       Exit: (11) takeout(3, [_G485, _G491, _G497, 3|_G501], [_G485, _G491, _G497|_G501]) ? creep
    

    第二种情况:

    X = [1, 2, 3] ;
       Redo: (8) takeout(1, _G451, [2, 3]) ? creep
       Call: (9) takeout(1, _G532, [3]) ? creep
       Exit: (9) takeout(1, [1, 3], [3]) ? creep
       Exit: (8) takeout(1, [2, 1, 3], [2, 3]) ? creep
       Exit: (7) perm([1, 2, 3], [2, 1, 3]) ? creep
    

    所以,谓词枚举的顺序其实很重要。在第一种情况下,您产生了许多具有未知值的状态。最好(尽可能)列出一份纸质清单,运行trace 并画出实际情况。

    但简而言之,在第一种情况下,您会产生大量带有takeout 事实的未知变量,这些变量无法与perm 匹配。

    【讨论】:

    • 是的。我得到了它。如果先外卖,就会有像 take(3, X, Y) 这样的调用。有无穷多个 X 和 Y 满足这个谓词,所以它是无解的。谢谢你的建议。
    【解决方案3】:

    Prolog 使用SLD resolution,因此子句中的子句顺序和文字顺序确实会有所不同。基本上,引擎尝试通过以深度优先的方式从上到下搜索来解析子句标题。换句话说,在声明性语义之上还有一个过程语义。这种区别有时会让初学者感到困惑,但另一方面,这也是 Prolog 真正成为一种编程语言(即图灵完备)的关键原因。

    【讨论】:

    • 另一方面,Mercury 没有这个属性,仍然是图灵完备的编程语言。但我更喜欢 Prolog 的简单性。
    • 因为水星有功能.. 一个很好的反例是数据记录,没有程序语义,没有功能,不是图灵完备
    • 我的意思是 Mercury 是一种没有定义搜索顺序的逻辑语言。
    【解决方案4】:

    您的基本情况 perm([],[]) 需要首先出现,否则它将继续下降到 perm 谓词,直到您用完堆栈空间。对于未来的谓词也要记住这一点,它在 prolog 中非常重要。

    另外,您可能应该在另一个谓词中切换 perm 和 takeout 的顺序。

    【讨论】:

    猜你喜欢
    • 2017-08-18
    • 2018-06-13
    • 2017-12-09
    • 1970-01-01
    • 2022-10-01
    • 2016-05-27
    • 2014-02-21
    • 1970-01-01
    相关资源
    最近更新 更多