【问题标题】:Understanding prolog better更好地理解序言
【发布时间】:2021-07-03 20:37:56
【问题描述】:

我正在尝试了解 Prolog 及其使用的解析算法。我找到了这个例子:

hates(1, 2).
hates(2, 3).
hates(3, 4).
jealous(A, B) :- jealous(A, C), jealous(C,B).
jealous(A,B) :- hates(A,B).

但是当我尝试说 jealous(1,4) 时,它会一直溢出并且永远不会产生 true,这很奇怪,好像 1 讨厌 2 和 2 讨厌 3 和 3 讨厌 4,那么 1 也应该讨厌 4。

但是我尝试改变它,所以它是这样的:

hates(1, 2).
hates(2, 3).
hates(3, 4).
jealous(A,B) :- hates(A,B).
jealous(A, B) :- jealous(A, C), jealous(C,B).

然后当我说jealous(1,4)时它就起作用了。

【问题讨论】:

    标签: prolog transitive-closure failure-slice


    【解决方案1】:

    然后当我说jealous(1,4)时它就起作用了

    不,它没有。或者,嗯,有点。只需键入 SPACE 即可看到它再次循环。那么立即循环的jealous(4,1) 呢?

    要理解这一点,只需看一下程序的一小部分就足够了,即这个:

    jealous(A,B) :- false, 讨厌(A,B)。 嫉妒(A,B):-嫉妒(A,C),false, jealous(C,B)

    注意变量B,它从未在可见部分中使用。所以它不会对终止产生任何影响。并注意 A 刚刚交给第一个目标。所以这两个论点在这个程序中都没有影响。因此,该程序从不终止。它可能会在这里和那里找到解决方案,但当被要求找到所有解决方案时它永远不会终止。

    这个小片段称为,如果它没有终止,你的整个程序也会终止!也就是说,根本不需要阅读您对hates/2 的定义1

    如果你想掌握 Prolog 的执行,你需要掌握故障片的概念。因为,有经验的程序员或多或少是凭直觉来做这件事的。因此,他们不会阅读整个程序,他们只是扫描相关部分。 Prolog 的好处在于,这样的切片与整个程序之间存在真正的因果关系。

    要解决此问题,您需要更改故障切片突出显示的部分中的某些内容。这是一种可能的变化:

    jealous(A,B) :- hates(A,B).
    jealous(A, B) :- hates(A, C), jealous(C,B).
    

    但是,一旦您在 hates/2 中有循环,这将不再有效。然后,考虑closure/3

    jealous(A, B) :-
       closure(hates, A, B).
    

    另一种方法是使用表格。但请注意,表格解决了这一问题,但不再适用于约束环境(迟早你会遇到)。


    1) 前提是你有一个纯单调程序。

    【讨论】:

      【解决方案2】:

      如果您使用的是 SWI-Prolog 或 XSB Prolog,您可以尝试添加:

      :- table jealous/2.
      

      它应该可以工作(我还没有尝试过 - 但这里有一个类似的例子:https://www.swi-prolog.org/pldoc/man?section=tabling-non-termination)。

      您的问题是 Prolog 默认执行策略,即从左到右深度优先。您可以通过将最后一个子句更改为:

      jealous(A, B) :- hates(A, C), jealous(C,B).
      

      (Prolog教科书中有很多类似的例子)

      【讨论】:

        猜你喜欢
        • 2017-12-08
        • 2016-04-09
        • 2018-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多