【问题标题】:How to determine whether two list have same element in prolog?如何确定两个列表在序言中是否具有相同的元素?
【发布时间】:2009-11-17 10:17:36
【问题描述】:

prolog中如何判断两个list是否有相同的元素? 如果我有两个列表 A 和 B,我想知道它们是否具有相同的元素。

【问题讨论】:

  • 你能写一个欲望行为的例子吗?
  • 编写一个 comp_list/2 当它们在列表中有相同元素时成功。
  • 定义“相同”,即您的意思是 = 或 == 还是别的什么?

标签: list prolog


【解决方案1】:

你需要写一个谓词。您可能会发现内置的 prolog member/2 很有用。

如果不给出答案,就很难再说下去了。想一想。你会明白的。

【讨论】:

    【解决方案2】:

    使用intersection/3怎么样?

    doesIntersect(X,Y) :-
        intersection(X,Y,Z),
        dif(Z,[]).
    

    如果intersection/3 产生一个空列表,那么这些列表没有共同点,结果为假。

    intersection/3 递归调用member/2

    intersection([X|Tail],Y,[X|Z]) :-
        member(X,Y),
        intersection(Tail,Y,Z).
    intersection([X|Tail],Y,Z) :-
        \+ member(X,Y),
        intersection(Tail,Y,Z).
    intersection([],_,[]).
    

    source.

    【讨论】:

    • 经验法则:混合dif/2(\+)/1 很可能不起作用:Y=a,doesIntersect([b,Y],[b]). 正确成功,但doesIntersect([b,Y],[b]),Y=a. 失败。所以你的程序不是关系。
    【解决方案3】:

    我们从基于内置谓词member/2 的纯实现开始:

    common_member(Xs,Ys) :-
       member(E,Xs),
       member(E,Ys).
    

    示例查询:

    ?- common_member([1,2,3],[1]).
      true
    ; false.
    
    ?- common_member([1,2,3],[4]).
    false.
    
    ?- common_member([1,2,3],[2,3,1]).
      true
    ; true
    ; true
    ; false.
    

    声明式地,上面的代码是可以的。 但是,它在成功时会留下无用的选择点。 此外,如果两个列表中存在多个项目,我们会得到多余的答案。

    我们能否在保持逻辑纯正的同时提高上述效率方面? 是的!

    但是怎么做呢?通过使用if_/3 和具体化的测试谓词memberd_t/3!

    common_memberd([X|Xs],Ys) :-
       if_(memberd_t(X,Ys), true, common_memberd(Xs,Ys)).
    

    让我们再次运行上面的示例查询,这次是common_memberd/2

    ?- common_memberd([1,2,3],[1]).
    true.
    
    ?- common_memberd([1,2,3],[4]).
    false.
    
    ?- common_memberd([1,2,3],[2,3,1]).
    true.
    

    冗余答案已被消除,随后的查询确定性地这样做。

    请注意,common_memberd/2 是纯粹的,因此即使对于非常一般的查询,我们也会得到正确的答案!

    ?- common_memberd([1,2,3],[A,B]).
          A=1
    ; dif(A,1),                         B=1
    ;               A=2 ,           dif(B,1)
    ; dif(A,1), dif(A,2),                         B=2
    ;                         A=3 , dif(B,1), dif(B,2)
    ; dif(A,1), dif(A,2), dif(A,3),                     B=3
    ; false.
    

    【讨论】:

    • 原来使用member/2的版本还是很吸引人的。难道没有更好的方法来解决这个问题吗?我特别不喜欢必须理解递归。两个列表之间的这种不对称性也可能会被消除。
    【解决方案4】:

    您可以首先构建一个谓词differs/2,以验证列表A 中的任何元素是否不是列表B 的成员。如果你能在A 上找到满足这个条件的元素,那么你可以肯定A 不包含在B 中。这实际上更容易验证A 的每个元素是否存在于B 中。
    使用内置谓词member/2differs/2 将如下所示:

    differs(T, Q):- 
             member(X,T), 
             not( member(X, Q)). 
    

    现在要证明两个列表包含相同的元素,您只需要验证它们不differs。使用与@repeat 相同的谓词名称(好奇,现在谁在重复?),这是我的common_memberd\2 谓词:

    common_memberd(T, Q):- 
                    not( differs(T, Q)), 
                    not( differs(Q, T)).
    

    咨询:

    ?- common_memberd( [2,3,4,1], [3, 1,4,2]).  
       true.  
    ?- common_memberd( [2,3,1,5], [3, 1,4,2]).  
       false.
    

    注意:无论元素的顺序或它们是否重复,此解决方案都有效。

    【讨论】:

    • common_memberd([1],[X]), X=2. 成功,但 X=2, common_memberd([1],[X]). 失败。
    • 好点,我想到了一个新的解决方案,使用递归,但仍然失败 common_memberd([X,Y, 1],[2,Z, 3]).
    • 你可以添加一个检查,比如说,groundness,即( ground(T+Q) -> true ; throw(error(instantiation_error,_)) ),或者使用@repeat的方法;或介于两者之间的使用 iso_dif/2 在术语实例化不充分的情况下也会产生 instantiation_error
    • 而且not/1经常被支持,而是(\+)/1
    【解决方案5】:

    如果这就是你要找的东西,请告诉我:

    same(T, Q) :- any(T, Q), !; any(Q, T), !.
    
    any([X|_], [X,_]):- !.
    any([X|T], Q) :- member(X, Q), !; any(T, Q), !.
    

    如果您咨询它:

    ?- same([1,2,3,4], [3]).
    true.
    
    ?- same([1,2,3,4], [4]).
    true.
    
    ?- same([1], [1,4]).
    true.
    
    ?- same([1,4], [1]).
    true.
    

    【讨论】:

    • 您实际上不需要为此编写递归谓词。成员已经为您完成了扫描列表以查找项目的工作。
    • @humble coffe:你能解释一下你的肯定吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-10
    • 2019-05-16
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多