我最近开始学习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).
有人能帮帮我吗?
答案 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)
和X
已Ys
为真。鉴于此,并且假设我们有一个与Y
不同的拟合X
。那么
我们可以得出结论,memberd(X, [Y|Ys])
也是如此。
因此,这消除了冗余解决方案。但是我们的定义仍然不是很有效:它仍然必须为每个元素访问Arg2两次,然后它无法断定没有剩余的替代品。无论如何:拒绝放置来删除它。
比较memberd/2
和non_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 -> 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方程的不完整决策程序,因此在每种情况下,该方法可能无法像这里一样平稳。
再见