【问题标题】:Prolog union for A U B U CA U B U C 的 Prolog 联合
【发布时间】:2015-02-06 03:15:33
【问题描述】:

我最近开始学习Prolog,但我无法解决如何将三个列表合并。

我能够合并 2 个列表:

%element
element(X,[X|_]).
element(X,[_|Y]):-
               element(X,Y).

%union

union([],M,M).
union([X|Y],L,S) :- element(X,L),union(Y,L,S).
union([X|Y],L,[X|S]) :- (not(element(X,L))),union(Y,L,S).

有人可以帮帮我吗?

【问题讨论】:

    标签: list prolog


    【解决方案1】:
    union(A, B, C, U) :-
       union(A, B, V),
       union(C, V, U).
    

    你对union/3的定义可以通过替换来改进

    ... not(element(X,L)), ...
    

    通过

    ... maplist(dif(X),L), ...
    

    ... non_member(X, L), ....
    
    non_member(_X, []).
    non_member(X, [E|Es]) :-
       dif(X, E),
       non_member(X, Es).
    

    这是一个差异显示的案例:

    ?- union([A],[B],[C,D]).
    A = C,
    B = D,
    dif(C, D).
    

    [A][B] 必须如何看起来才能使它们的并集包含 2 个元素?

    答案是:它们一定是不同的。

    您的原始版本在此查询中失败,但对于如下特殊实例却成功:

    ?- A = 1, B = 2, union([A],[B],[C,D]).
    

    因此它成功了,但无法概括它。因此它不是一个纯粹的逻辑关系。

    那么dif/2 一切都很好吗?不幸的是没有。 @TudorBerariu 有充分的理由去削减,因为它反映了我们对这种关系的一些意图。削减有效地反映了两个关键意图

    • 现在排除了不作为成员的替代方案,这对于某些模式是正确的,例如 Arg1 和 Arg2 都是充分实例化的术语。一个安全的近似值是基本项。

    • 无需查看列表 Arg2 中的其他元素,这也仅在 Arg1 和 Arg2 充分实例化时才成立。

    只有在术语没有充分实例化时才会出现问题..

    OP 的定义和上述定义的缺点是两者都过于笼统,可以通过 Arg2 中的重复元素观察到:

    ?- union([a,a],[a,a],Zs).
    Zs = [a, a] ;
    Zs = [a, a] ;
    Zs = [a, a] ;
    Zs = [a, a] ;
    false.
    

    事实上,我们得到 |Arg2||Arg1|-1 个冗余答案。因此,削减有一些很好的理由。

    目前union/3 效率不高的另一个原因是,对于(预期的)基本情况,它留下了不必要的选择点。同样,@TudorBerariu 的解决方案没有这个问题:

    ?- union([a],[a],Zs).
    Zs = [a] ;    %    <--- Prolog does not know that there is nothing left.
    false.
    

    消除冗余

    许多多余答案的真正罪魁祸首是第一条规则。 element(a,[a,a])(俗称member/2)会成功两次。

    union([X|Y],L,S) :- element(X,L), union(Y,L,S).
                        ^^^^^^^^^^^^
    

    这是一个改进的定义:

    memberd(X, [X|_Ys]).
    memberd(X, [Y|Ys]) :-
       dif(X,Y),          % new!
       memberd(X, Ys).
    

    递归规则,从右到左阅读,如下:

    假设memberd(X, Ys) 对于某些XYs 已经成立。鉴于此,并且我们有一个合适的Y,它不同于X。那么


    我们可以得出结论,memberd(X, [Y|Ys]) 也是正确的。

    所以这消除了多余的解决方案。但是我们的定义仍然不是很有效:它仍然要对每个元素访问 Arg2 两次,然后无法得出没有备选方案的结论。在任何情况下:拒绝切割将其移除。

    通过具体化引入确定性。

    比较memberd/2non_member/2 的定义。尽管它们描述了彼此的“相反”,但它们看起来非常相似:

    non_member(_X, []).
    non_member(X, [Y|Ys]) :-
       dif(X,Y),
       non_member(X, Ys).
    
    memberd(X, [X|_Ys]).
    memberd(X, [Y|Ys]) :-
       dif(X,Y),         
       memberd(X, Ys).
    

    递归规则是一样的!只有事实不同。让我们将它们合并到一个定义中——用一个额外的参数来判断我们是指memberd (true) 还是non_member (false):

    memberd_t(_X, [], false).
    memberd_t(X, [X|_Ys], true).
    memberd_t(X, [Y|Ys], Truth) :-
       dif(X, Y),
       memberd_t(X, Ys, Truth).
    

    现在,我们的定义变得更紧凑了:

    unionp([], Ys, Ys).
    unionp([X|Xs], Ys, Zs0) :-
      if_( memberd_t(X, Ys), Zs0 = Zs, Zs0 = [X|Zs] ),
      unionp(Xs, Ys, Zs).
    
    memberd_t(_X, [], false).          % see below
    memberd_t(X, [Y|Ys], Truth) :-
       if_( X = Y, Truth=true, memberd_t(X, Ys, Truth) ).
    

    注意if_(If_1, Then_0, Else_0) 和 if-then-else 控制结构( If_0 -&gt; Then_0 ; Else_0 ) 之间的区别。虽然 If_1 可能会成功多次并具有不同的真值(即,它可以同时为真和假),但控制结构使 If_0 仅成功一次,因为它只为真。

    if_(If_1, Then_0, Else_0) :-
       call(If_1, T),
       (  T == true -> call(Then_0)
       ;  T == false -> call(Else_0)
       ;  nonvar(T) -> throw(error(type_error(boolean,T),_))
       ;  /* var(T) */ throw(error(instantiation_error,_))
       ).
    
    =(X, Y, T) :-
       (  X == Y -> T = true
       ;  X \= Y -> T = false
       ;  T = true, X = Y
       ;  T = false,
          dif(X, Y)                             % ISO extension
          % throw(error(instantiation_error,_)) % ISO strict
       ).
    
    equal_t(X, Y, T) :-
       =(X, Y, T).
    

    为确保memberd_t/3 始终从第一个参数索引中受益,请使用以下定义(感谢@WillNess):

    memberd_t(E, Xs, T) :-
       i_memberd_t(Xs, E, T).
    
    i_memberd_t([], _E, false).
    i_memberd_t([X|Xs], E, T) :-
       if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
    

    【讨论】:

    • 你错过了那里的第二个参数:maplist(dif(X), L)。很好的答案!
    • @TudorBerariu:谢谢!我什至可以省略L,这不是更高的顺序。
    • @false 奇怪的行为:gist.github.com/WillNess/cf47a4117331f949fbd9 (?) -- 我不自动加载 clpfd,也许这就是原因?...(在 SWI-Prolog 64 位、7.2.0、Win7PRO 中运行) .
    • @WillNess:丑,谢谢! SWI 中的多参数索引启发式似乎已经改变。因为,memberd_t(E, [3], T), E = 1. 是确定的,但 E = 1, memberd_t(E, [3], T) 不是。所以我添加了也适用于 SICStus 的第一个参数索引版本。
    • @WillNess:查看this了解更复杂的情况。
    【解决方案2】:

    您可以将前两个列表合并,然后将该结果与第三个列表合并:

    union(L1, L2, L3, U):-union(L1, L2, U12), union(U12, L3, U).
    

    您可以使用剪切运算符改进union/3

    union([],M,M).
    union([X|Y],L,S) :- element(X,L), !, union(Y,L,S).
    union([X|Y],L,[X|S]) :- union(Y,L,S).
    

    【讨论】:

    • 这工作得很好,但我需要写 union(L1, L2, L3, U):-union(L1, L2, U12), union(U12, L3, U)。作为谓词
    • 我不明白。为什么那个条款不适合你?
    【解决方案3】:

    仅使用带有额外参数的谓词,例如memberd_t/3,只会导致弱具体化。对于强具体化,我们还需要生成约束。强具体化是消除非确定性的进一步方法。

    但是强具体化是困难的,归档它的一种可能方法是使用CLP(*) 实例,它也具体化了逻辑运算符。这是一个使用CLP(FD) 解决联合问题的示例。不幸的是,这仅涵盖域Z

    强具体化代码:

    member(_, [], 0).
    member(X, [Y|Z], B) :-
       (X #= Y) #\/ C #<==> B,
       member(X, Z, C).
    
    union([], X, X).
    union([X|Y], Z, T) :-
       freeze(B, (B==1 -> T=R; T=[X|R])),
       member(X, Z, B),
       union(Y, Z, R).
    

    上面没有不必要的选择点。这里有一些例子表明这种情况不再发生:

    跑地面示例:

    ?- union([1,2],[2,3],X).
    X = [1, 2, 3].
    

    如果我们在某处使用变量,上述示例甚至不会创建选择点。但我们可能会看到很多限制:

    运行非接地示例:

    ?- union([1,X],[X,3],Y).
    X#=3#<==>_G316,
    1#=X#<==>_G322,
    _G316 in 0..1,
    freeze(_G322,  (_G322==1->Y=[X, 3];Y=[1, X, 3])),
    _G322 in 0..1.
    
    ?- union([1,X],[X,3],Y), X=2.
    X = 2,
    Y = [1, 2, 3].
    

    由于我们没有制定一些输入不变量,解释器无法看出在上述情况下产生约束没有任何意义。我们可以使用all_different/1 约束来帮助解释一下:

    提供不变量:

    ?- all_different([1,X]), all_different([X,3]), union([1,X],[X,3],Y).
    Y = [1, X, 3],
    X in inf..0\/2\/4..sup,
    all_different([X, 3]),
    all_different([1, X]).
    

    但我们不应该对这个单一的例子抱太大期望。由于 CLP(FD)freeze/2 只是命题和 Z 方程的不完整决策过程,因此该方法可能不会在任何情况下都像这里那样顺利。

    再见

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-31
      • 2018-09-24
      • 2014-11-30
      • 2017-12-28
      • 2018-10-21
      • 2020-09-28
      相关资源
      最近更新 更多