【问题标题】:Prolog: Rules evaluating in an unexpected orderProlog:以意外顺序评估的规则
【发布时间】:2013-11-23 21:36:43
【问题描述】:

我目前正在开发一个 Prolog 程序以从列表中删除重复项。

这是我的代码:

makeset([First], [First]). % if only one item in list, the solution is itself.
makeset([First, Second], [First, Second]) :-
   dif(First, Second).
makeset([First, Second], [First]) :-
    First = Second.
% if more than two items in list
makeset([First | Tail], FinalList) :-
   member(First, Tail),
   makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
   not(member(First, Tail)),
   makeset(Tail, [First |FinalList]).

但是,当我在包含两个以上项目的列表上尝试此操作时,它只会返回 false。我进行了跟踪,发现它继续使用最后两个规则,即使它下降到列表中的两个项目。这是为什么呢?

查询示例:makeset([3, 2, 4, 5, 1], X). 我返回错误,但每次评估都使用最后一条规则。

编辑: 我将规则更改为以下规则,但仍然无法正确评估,因此问题必须与最后一条规则有关。

makeset([First], FinalList) :- 
 FinalList = [First]. % if only one item in list, the solution is itself.
makeset([First, Second], FinalList) :- 
 not(First = Second), FinalList = [First, Second].
makeset([First, Second], FinalList) :- 
 First = Second, FinalList = [First].
% if more than two items in list
makeset([First | Tail], FinalList) :- 
 member(First, Tail), makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :- 
 not(member(First, Tail)), makeset(Tail, [First |FinalList]).

我该如何修正最终规则?

【问题讨论】:

    标签: list prolog rule


    【解决方案1】:

    净化你的代码!

    首先。看来你是在坚持这个世界纯粹的一面。当然,您使用的是dif/2!与它的程序对应物相比,它丑陋的兄弟(\=)/2 这意味着此时无法统一,但可能稍后,或者永远不会,不要问我dif/2 谓词有一个纯陈述意义。它意味着(句法或理论无关)不平等。这不仅会给你带来业力和放纵。不,它甚至可以帮助您调试程序。事实上,我们可以使用 Prolog 来帮助我们找出程序中的问题——只要你的代码足够纯!所以首先,让我们净化你的代码,然后我们可以调试它。

    您的代码中唯一不正确的是not(member(First, Tail))。我将用maplist(dif(First),Tail) 替换它。现在我们有了一个纯程序。它失败了,就像以前一样。首先可能是减少列表的大小。你真的想经历那件苦差事吗?这是一个更简单的方法,只适用于纯 Prolog 程序。我会问:

    显示所有可以想象的列表,它们的集合是什么。

    好的,所有列表。不会有人掉队。

    问 Prolog!

    | ?-长度(L,I),makeset(L,X)。 我 = 1,L = [_A],X = [_A] ? ; I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 2, L = [_A,_A], X = [_A] ? ; I = 2, L = [_A,_A], X = [_A] ? ...

    这已经够糟糕的开始了!首先,I = 0 没有解决方案。让我们添加事实makeset([], []),然后I = 2 有这个冗余。可以去掉makeset([First, Second], [First]) :- First = Second.这个规则,现在我们再问一遍:

    | ?-长度(L,I),makeset(L,X)。 我 = 0, L = [], X = [] ? ; 我 = 1,L = [_A],X = [_A] ? ; I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 2, L = [_A,_A], X = [_A] ? ; I = 3, L = [_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 3, L = [_A,_A,_A], X = [_A] ? ; I = 3, L = [_A,_B,_A], X = [_B,_A], prolog:dif(_B,_A) ? ; I = 3, L = [_A,_A,_A], X = [_A] ? ; I = 4, L = [_A,_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ...

    I in 0..2 的所有答案现在看起来都很完美。它们包含所有可以想象的列表。他们都是。请注意,在一个答案中,dif/2 再次出现,确保元素不同。

    对于I = 3,事情变得更加复杂。一些观察:

    • X = [_,_,_] 没有答案。发现错误!事实上,发现了最小的错误!

    • L = [_A,_A,_A] 有多余的答案

    我们可以将第一个问题简化为一个目标:

    | ?- L = [_,_,_], L = X, makeset(L, X).
    no
    

    应该有答案。我们现在可以应用另一种策略:

    概括您的代码

    显然makeset/2 的定义太专业了。我现在将尝试对其进行概括以更好地定位错误。为此,将以下内容添加到您的程序中:

    :- op(950,fy, [*]).
    
    *_.
    

    通过这种方式,我们可以在目标前添加* 以将其删除。我们也可以为此使用 cmets,但这是一种更简洁的方法。所以我现在要做的就是系统地添加*,然后看看目标是否仍然失败。此外,我将一些变量重命名为新名称(_x),这相当于进一步概括。经过一番尝试,我最终得到:

    :- op(950,fy, [*])。 *_。 组件([],[])。 makeset([First], [_xFirst])。 makeset([First, Second], [_xFirst, _xSecond]) :- * dif(第一,第二)。 makeset([First | Tail], FinalList) :- * member(First, Tail), makeset(尾巴,FinalList)。 makeset([First | Tail], FinalList) :- * maplist(dif(First),Tail), makeset(尾巴,[_xFirst |FinalList])。 ?- L = [_,_,_], L = X, makeset(L, X)。

    在这个程序中,实际元素不再相关。因此,它只是列表的长度!所以问题一定与处理长度的方式有关。仅当存在第二个参数是更长列表的解决方案时,最后一条规则才适用。这不可能。所以问题是最后一条规则。该列表应该出现在头部。

    现在,出现了新的冗余。最终定义如下:

    makeset([], []).
    makeset([First | Tail], FinalList) :-
       member(First, Tail),
       makeset(Tail, FinalList).
    makeset([First | Tail], [First|FinalList]) :-
       maplist(dif(First),Tail),
       makeset(Tail, FinalList).
    

    有更聪明的方式来表达这种member/maplist dif二分法。但我就这样放着吧……

    【讨论】:

      【解决方案2】:
      makeset([First | Tail], [First | FinalList]) :- 
       not(member(First, Tail)), makeset(Tail, FinalList).
      

      您不应将First 作为makeset 的输入的一部分,这会阻止之前的规则匹配。仅应在使用 makeset 谓词统一 FinalList 后将 First 添加到列表中。

      当输入列表为空时,您的解决方案也无法处理。

      【讨论】:

      • 完美,效果很好。我还将处理空列表。谢谢!
      【解决方案3】:

      在最后一条规则中,您将元素从第一个列表移动到第二个。 然后你阻止匹配顶级规则。

      注意:dif/2 是一个强大但复杂的谓词。你真的需要它的力量吗?

      【讨论】:

      • 我开始使用 dif 是因为使用 not (first = second) 似乎无法正常工作。
      • First \= Secondnot(First = Second) 都可以,尽管它通常写成\+(First = Second)。这只有在您检查包含变量的列表时才会有所不同。
      • 我改变了前几条规则,但仍然不匹配,所以我更新了问题,问最后的规则应该如何改变。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2021-02-14
      • 1970-01-01
      • 2011-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多