A U B U C的Prolog联盟

时间:2014-12-08 12:51:05

标签: list prolog

我最近开始学习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).

有人能帮帮我吗?

3 个答案:

答案 0 :(得分:22)

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).

递归规则是一样的!只有事实是不同的。让我们将它们合并为一个定义 - 用另一个参数来表明我们是指memberdtrue)还是non_memberfalse):

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 -> 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) ).

答案 1 :(得分:8)

您可以将前两个列表的并集,然后是该结果与第三个列表之间的并集:

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).

答案 2 :(得分:1)

仅使用带有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方程的不完整决策程序,因此在每种情况下,该方法可能无法像这里一样平稳。

再见