List A-U-B-U-C的Prolog联合

List A-U-B-U-C的Prolog联合,list,prolog,List,Prolog,我最近开始学习Prolog,但我无法解决如何将三个列表合并 我能够将两个列表合并: %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). 有人能

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

我能够将两个列表合并:

%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).
有人能帮我吗

union(A, B, C, U) :-
   union(A, B, V),
   union(C, V, U).
您对
union/3
的定义可以通过替换

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

以下是一个不同的例子:

?- union([A],[B],[C,D]).
A = C,
B = D,
dif(C, D).
[A]
[B]
的并集包含两个元素,它们的外观必须是什么样的

答案是:它们一定不同

您的原始版本对此查询失败,但对于以下特殊实例,它会成功:

?- 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.
以下是一个改进的定义:

memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
   dif(X,Y),          % new!
   memberd(X, Ys).
从右向左读取的递归规则如下所示:

假设
memberd(X,Ys)
对于某些
X
Ys
已经为真。假设我们有一个与
X
不同的拟合
Y
。然后我们可以得出结论,memberd(X,[Y | Ys])也是正确的

因此,这消除了冗余解决方案。但是我们的定义仍然不是很有效:它仍然需要为每个元素访问Arg2两次,然后它无法得出没有其他选择的结论。在任何情况下:抵制放置切口以移除该切口

通过物化引入决定论。 比较
成员D/2
非成员/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
):

现在,我们的定义变得更加简洁:

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,然后_0,Else_0)
和if-Then-Else控件构造
(if_0->Then_0;Else_0)
之间的差异。虽然
If_1
可能会使用不同的真值成功多次(也就是说,它可以是true和false),但控制构造使
If_0
仅成功一次,因为它仅为true

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\u t/3
始终从第一个参数索引中获益,请使用以下定义(感谢@WillNess):


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

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

仅使用带有额外参数(如)的谓词只会导致弱具体化。对于强物化,我们还需要生成约束。强物化是消除非决定论的进一步途径

但是强具体化是困难的,一种可能的归档方法是使用
CLP(*)
实例,该实例还具体化了逻辑运算符。下面是一个使用
CLP(FD)
解决联合问题的示例。不幸的是,这只涉及域
Z

strong具体化代码:

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,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]).
此外,如果我们在某个地方使用变量,上面的示例甚至不会创建选择点。但我们可能会看到很多限制:

运行非地面示例:

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,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方程的一个不完整的决策过程,因此该方法可能不会在所有情况下都像这里那样顺畅


再见

这很好,但我需要编写union(L1,L2,L3,U):-union(L1,L2,U12),union(U12,L3,U)。我不明白。为什么该子句不适合您?您错过了第二个参数:
maplist(dif(X),L)
。回答得很好@图多贝拉里:谢谢你!我甚至不能忽略
L
@错误的奇怪行为:(?)--我不自动加载clpfd,也许这就是原因吧?。。。(在SWI Prolog 64位、7.2.0、Win7PRO中运行)。@WillNess:丑陋,thx!SWI中的多参数索引启发法似乎已经改变。对于,
memberd\u t(E[3],t),E=1。
是确定的,但
E=1,memberd\u t(E[3],t)
不是。因此,我添加了第一个参数索引版本,该版本也适用于SICStus。@WillNess:有关更复杂的情况,请参阅。
?- 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]).