【问题标题】:Prolog - Finding adjacent elements in a listProlog - 在列表中查找相邻元素
【发布时间】:2016-06-10 13:53:52
【问题描述】:

我正在尝试定义一个谓词adjacent(X, Y, Zs),如果 X 和 Y 在列表中相邻,则该谓词为真。我的代码目前是这样的:

adjacent(_, _, []).
adjacent(X, Y, [X, Y|Tail]) :-
  adjacent(X,Y, Tail).

它适用于adjacent(c, d, [a, b, c, d, e]) 的基本情况,但由于基本情况,所有其他情况也返回 true,我坚持这一点。

另一个问题是,如果 X 不等于列表头部的第一部分,那么它会跳过 X 和 Y 并转到下一个“X”;例如,如果 c 不等于 a,那么它会跳过 ab 并检查是否c 等于 c。这是有问题的,例如,列表是

[a, c, d, e]

因为它最终从不检查 c(我相信)。

我不知道如何调和这两个问题,并将我对需要发生的事情的逻辑理解转化为代码。

编辑:感谢 Christian Hujer 的回答,我的基本情况错误已得到纠正,所以现在我只停留在第二个问题上。

【问题讨论】:

标签: list recursion prolog traversal


【解决方案1】:

我认为从长远来看,这是一个比@repeat 的解决方案更可取的定义:

adjacent(X0, X1, [E0,E1|Es]) :-
   adjacent_(Es, E0, E1, X0, X1).

adjacent_([],      E0, E1, E0, E1).
adjacent_([E2|Es], E0, E1, X0, X1) :-
   if_(( E0 = X0, E1 = X1 ),
       true,
       adjacent_(Es, E1, E2, X0, X1)).

使用具体化的和:

','(A_1, B_1, T) :-
   if_(A_1, call(B_1, T), T = false).

;(A_1, B_1, T) :-
   if_(A_1, T = true, call(B_1, T)).

【讨论】:

    【解决方案2】:

    辅助谓词adjacent_/5 总是“落后”两个(列表项):

    相邻(X0,X1,[E0,E1|Es]):- 相邻_(Es,E0,E1,X0,X1)。 相邻_([],E0,E1,E0,E1)。 相邻_([E2|Es],E0,E1,X0,X1):- if_(E0+E1 = X0+X1, 真的, 相邻_(Es,E1,E2,X0,X1))。

    使用 SWI-Prolog 我们运行:

    ?- set_prolog_flag(double_quotes, chars)。 真的。 ?- 相邻(a,b,“abab”)。 真的。 ?- 相邻(b,c,“abcd”)。 真的。 ?- 相邻(X,Y,“abcd”)。 X = a, Y = b ; X = b,Y = c ; X = c,Y = d。

    编辑

    adjacent_/5 的更正定义也为以下查询提供了正确答案:

    ?- 相邻(X,X,[A,B,C])。 X = A,A = B ; X = B,B = C,差异(f(C,C),f(A,A))。 ?- 相邻(a,X,“aab”)。 X = 一个 ; X = b。 ?- 相邻(a,b,“aab”)。 真的。

    【讨论】:

    • adjacent(a,b,"aab"). 失败
    • 现在的新定义(从长远来看)效率非常低:它本质上需要创建这两个辅助结构f/2
    【解决方案3】:

    在原来的解决方案尝试中:

    adjacent(_, _, []).
    adjacent(X, Y, [X, Y|Tail]) :-
        adjacent(X,Y, Tail).
    

    正如@ChristianHujer 指出的那样,第一个子句不应该存在,因为它不是真的。空列表不应有相邻元素。

    第二个子句也有问题。它表明XY 在列表中是相邻的,但随后递归并且不只是成功。一个合适的子句应该是:

    adjacent(X, Y, [X,Y|_]).
    

    这表示XY 如果它们是列表中的前两个元素,则它们在列表中是相邻的,无论尾部是什么。这也形成了一个适当的基本情况。那么你的一般递归子句应该处理其余的情况:

    adjacent(X, Y, [_|Tail]) :-
        adjacent(X, Y, Tail).
    

    这表示XY[_|Tail] 中相邻,如果它们在Tail 中相邻。这可以解决您遇到的第二个问题。

    因此,整个解决方案是:

    adjacent(X, Y, [X,Y|_]).
    adjacent(X, Y, [_|Tail]) :-
        adjacent(X, Y, Tail).
    

    XY 按此顺序一起出现在列表中时,这将成功多次。


    这也可以通过 DCG 自然解决(尽管@repeat 的基于append/3 的解决方案更简洁):
    adjacent(X, Y) --> ..., [X, Y], ... .
    ... --> [] | [_], ... .
    
    adjacent(X, Y, L) :- phrase(adjacent(X, Y), L).
    

    | ?- adjacent(b, c, [a,b,c,d]).
    
    true ? a
    
    (1 ms) no
    | ?- 
    

    【讨论】:

    • prolog-cut 的变体在与某些模式一起使用时不完整/不健全。这将很多责任推给了潜在用户!
    • @repeat 是的,感谢您指出有关剪辑的内容。我刚刚删除了那个案例,因为它有点超出了 OP 要求的范围。
    • 是什么让您相信 repeat 的解决方案更清洁? adjacent(a,b,[a,b|non_list]) 成功了,我们显然有一个非列表!
    • @false "cleaner" 可能不是一个恰当的形容词。我可能应该说“更简洁”(已编辑)。
    • @lurker:请注意赏金以产生更好的版本(最好在新答案中)。
    【解决方案4】:

    在这个答案中,我们尽量保持简单——以append/3为基础:

    相邻(E0,E1,Es):- append(_, [E0,E1|_], Es)。

    示例查询:

    ?- adjacent(X, Y, [a,b,c,d,e]).
    X = a, Y = b ;
    X = b, Y = c ;
    X = c, Y = d ;
    X = d, Y = e ;
    false.
    

    【讨论】:

    • 这太棒了,但我有点不清楚如何阅读append 语句,也有点犹豫是否使用内置谓词,因为这是用于家庭作业。另外,如果我使用这样的东西,我仍然会保留adjacent(X, Y, [X,Y]) 的基本情况,对吗?
    • @quantumferret 目标append(_, [E0,E1|_], Es) 的读法如下:“列表Es 的后缀以E0 开头,后跟E1。”
    • @quantumferret 请注意,您建议的“基本情况”过于具体。应该是adjacent(X, Y, [X,Y|_])
    • 嗯,好的。谢谢你,你帮了大忙!
    【解决方案5】:

    我认为你的基本情况是错误的。在您的情况下,您希望递归以假谓词而不是真谓词终止。这是合乎逻辑的:在一个空列表中,没有相邻的元素。从不。

    【讨论】:

    • 哦,对了,如果 X 和 Y 是列表中仅有的两个元素,那么真实结果的基本情况是,对吗?
    • 所以基本情况位是固定的,现在我只是想弄清楚如果第一个不等于 X,如何防止它跳过一个元素,这是我的有点难过。
    • 尽管如此,@quantumferret 在他的尝试中使用[] 有一定的意义:为了确保第三个参数是一个列表,我们不知何故需要[]。否则像adjacent(a,b,[a,b|non_list]) 这样的意外解决方案是可能的。
    猜你喜欢
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 2011-08-17
    • 2015-11-26
    相关资源
    最近更新 更多