您的代码看起来有点奇怪,因为最后一条规则需要三个参数。您只调用二进制版本,因此没有递归会尝试派生它。
您已经有了一个好主意来查看列表中元素发生变化的部分。所以有4种情况:
1) 你的清单是空的。2)你只有一个元素。3)您的列表以两个相等的元素开始。4)您的列表以两个不同的元素开始。
没有指定案例 1,因此您可能需要为此找到一个明智的选择。案例 2 与案例 4 有点相似,因为列表的末尾可以看作是元素的变化,您需要附加两个副本,然后就完成了。案例 3 非常简单,我们可以只保留元素并递归其余元素。案例 4 是您需要再次插入两个副本的地方。
这意味着您的代码将如下所示:
% Case 1
dbl([],[]).
% Case 2
dbl([X],[X,X,X]).
% Case 3
dbl([X,X|Xs], [X|Ys]) :-
% [...] recursion skipping the leading X
% Case 4
dbl([X,Y|Xs], [X,X,X|Ys]) :-
dif(X,Y),
% [...] we inserted the copies, so recursion on [Y|Xs] and Ys
案例 3 应该很容易完成,我们只需从两个列表中删除第一个 X 并递归 dbl([X|Xs],Ys)。请注意,我们通过两次写入相同的变量来隐式地使前两个元素相等(即我们统一它们)。
如果您查看案例4的头部,您可以直接模仿您描述的模式:假设列表以X开头,然后是Y并且它们不同(dif(X,Y)),X重复3次而不是仅仅复制,然后我们继续对从 Y 开始的其余部分进行递归:dbl([Y|Xs],Ys)。
所以让我们试试谓词:
?- dbl([a,b,a,a,a,c,c],[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]).
true ;
false.
我们的测试用例被接受(真),我们没有找到一个以上的解决方案(假)。让我们看看我们是否找到了错误的解决方案:
?- dif(Xs,[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]), dbl([a,b,a,a,a,c,c],Xs).
false.
不,那也很好。如果我们的列表中有变量会发生什么?
?- dbl([a,X,a],Ys).
X = a,
Ys = [a, a, a, a, a] ;
Ys = [a, a, a, X, X, X, a, a, a],
dif(X, a),
dif(X, a) ;
false.
要么 X = a,则 Ys 是 5 as 的单次运行;或者 X 不等于 a,那么我们需要在所有三个运行中附加副本。看起来也不错。(*)
现在让我们看看,如果我们只指定解决方案会发生什么:
?- dbl(X,[a,a,a,b,b]).
false.
对,只有两个 bs 的列表不可能是我们规范的结果。所以让我们尝试添加一个:
?- dbl(X,[a,a,a,b,b,b]).
X = [a, b] ;
false.
万岁,它成功了!所以让我们作为最后一个测试看看会发生什么,如果我们只用两个变量调用我们的谓词:
?- dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G15],
Ys = [_G15, _G15, _G15] ;
Xs = [_G15, _G15],
Ys = [_G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15, _G15] ;
[...]
似乎我们得到了正确的答案,但我们只看到单次运行的案例。这是prolog的搜索策略的结果(我不会在这里解释)。但是,如果我们在生成较长的列表之前先查看较短的列表,我们可以看到所有解决方案:
?- length(Xs,_), dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G16],
Ys = [_G16, _G16, _G16] ;
Xs = [_G16, _G16],
Ys = [_G16, _G16, _G16, _G16] ;
Xs = [_G86, _G89],
Ys = [_G86, _G86, _G86, _G89, _G89, _G89],
dif(_G86, _G89) ;
Xs = [_G16, _G16, _G16],
Ys = [_G16, _G16, _G16, _G16, _G16] ;
Xs = [_G188, _G188, _G194],
Ys = [_G188, _G188, _G188, _G188, _G194, _G194, _G194],
dif(_G188, _G194) ;
[...]
所以看起来我们有一个有效的谓词(**),假设你从文本中填写了缺失的目标:)
(*) 这里要说明一下:这种情况只有效,因为我们使用的是 diff。第一个具有相等性的谓词,通常遇到的是 =、== 和它们各自的否定 \= 和 \==。= 代表可统一性(在参数 st 中替换变量它们变得相等), == 代表句法相等(术语完全相等)。例如:
?- f(X) = f(a).
X = a.
?- f(X) \= f(a).
false.
?- f(X) == f(a).
false.
?- f(X) \== f(a).
true.
这意味着,如果我们用 a 代替 X,我们可以使 f(X) 等于 f(a)。这意味着如果我们问它们是否不能相等(\=),我们会得到错误的答案。另一方面,这两个项不相等,所以 == 返回 false,它的否定 \== 返回 true。
这也意味着 X \== Y 总是正确的,所以我们不能在我们的代码中使用 \==。与此相反,dif 等待直到它可以决定它的参数是否相等。如果在找到答案后仍未决定,则打印“dif(X,a)”语句。
(**) 最后一句话:还有一个带有 if-then-else 构造的解决方案 (test ->goals_if_true;goals_if_false,它合并了案例 3 和 4。由于我更喜欢这个解决方案,您可能需要查看其他版本自己。