Prolog - 检查两个列表是否具有相同的元素,除了一个

时间:2016-05-23 07:07:04

标签: list prolog prolog-dif

我正在处理两个字符列表,我想检查它们是否具有相同的元素,除了处于相同位置的元素,如下所示:

compare([L1,L2,L3,L4],[W1,W2,W3,W4]) :-
((W1 \= L1, W2 = L2, W3 = L3, W4 = L4);
(W1 = L1, W2 \= L2, W3 = L3, W4 = L4);
(W1 = L1, W2 = L2, W3 \= L3, W4 = L4); 
(W1 = L1, W2 = L2, W3 = L3, W4 \= L4)).

这是有效的,但有一种简化的方法吗?

感谢。

2 个答案:

答案 0 :(得分:4)

使用 if_/3 and (=)/3我们可以@Steven's code单调:

cmp_([X|Xs], [Y|Ys]) :-
   if_(X = Y, cmp_(Xs, Ys), Xs = Ys).

答案 1 :(得分:3)

你是对的,即使你的解决方案有效,它也不是很干净也不可重复使用,因为它只适用于长度为4的列表。让我们尝试定义一个递归谓词,适用于任何规模的列表。

当两者同时列出一个元素时,实际上只有两种情况要考虑:元素是相同的还是不是。

如果它们相同,则意味着两个列表的其余部分必须只有一个不同的元素才能成功。而这正是我们首先编写的谓词!

compare([H|T1], [H|T2]) :- compare(T1, T2).

现在是第二个案例。如果列表的第一个元素不同,那么两个列表的其余部分必须完全相同(因为我们已经遇到了不同的元素

compare([H1|T1], [H2|T1]) :- H1 \= H2.

那就是全部!现在,您可能会注意到这样的输出:

?- compare([a,b], [a,c]).
true;
false.

这是因为仍有一个选择点是开放的:对于第一个元素匹配的第一个元素,但第二个子句尚未被考虑。但在这种情况下,我们知道这两个条款是互斥的。所以我们可以添加一个剪切(!)以确保没有选择点。

这也允许我们简化第二次关闭:如果我们达到这个,我们知道第一个元素不相同,所以不需要再次检查。

将所有内容放在一起,代码变为:

compare([H|T1], [H|T2]) :- !, compare(T1, T2).
compare([_|T], [_|T]).