我的代码按以下方式逐项合并两个列表列表:
mergeL([[a,b],[c,d]], [[1,2],[3,4]], Result). Result = [[a,b,1,2],[c,d,3,4]]
这是我使用的代码:
mergeL([],[],[]).
mergeL(List, [], List).
mergeL([], List, List).
mergeL([X|Rest],[Y|Rest2], [XY|Res2]) :-
mergeL(Rest, Rest2, Res2),
append(X,Y,XY).
这似乎有效,但如果我用两个相同大小的列表调用它,我会得到三个重复的结果。示例(两个列表只包含一个元素):
?- mergeL([[a,b]],[[1,2,3]],Q).
Q = [[a, b, 1, 2, 3]] ;
Q = [[a, b, 1, 2, 3]] ;
Q = [[a, b, 1, 2, 3]].
是否有一种干净的方法可以使这个输出只有一个解决方案?
答案 0 :(得分:3)
已经有3个SO答案,但我不能同意一个答案!它们都是不正确的。
考虑你定义中的三个事实:
mergeL([],[],[]). mergeL(List, [], List). mergeL([], List, List).
他们都成功mergeL([],[],[])
,这是你裁员的来源。第二个和第三个事实是List
是一个非空列表。所以让我们将其添加到定义中:
mergeL([],[],[]). mergeL(List, [], List) :- List = [_|_]. mergeL([], List, List) :- List = [_|_].
这消除了冗余解决方案。无需切割即可删除冗余解决方案。然而,other SO-answer中引入的削减可能会隐藏解决方案。对于查询mergeL(Xs,YS,Zs)
,切割版本只有一个解决方案,但应该有无限多个。
然而,使用剪辑有一些意义:他们能够删除一个选择点。但是这样的削减需要得到适当的保护:
mergeL(Xs, Ys, Zs) :- ( Xs == [], Ys == [] -> ! ; Zs == [] -> ! ; true ), Xs = [], Ys = [], Zs = []. ...
我不确定这是否值得付出努力......实施可能会更有效地提供此功能。有关详细信息,请参阅 this和this。
对你来说更有趣的可能是最后一条规则的改变。它应该读取:
mergeL([X|Rest],[Y|Rest2], [XY|Res2]) :- append(X,Y,XY), mergeL(Rest, Rest2, Res2).
这可以避免临时分配本地堆栈空间。所以这绝对是一种优化。但是优化不会损害谓词的逻辑读取。
在我的2009年32位笔记本电脑(几乎是蒸汽驱动)和SWI 6.3.3-40-g064f37b:
原始版本:
?- N is 2^20, length(L,N), maplist(=([]),L), time(mergeL(L,L,J)). % 2,097,206 inferences, 5.232 CPU in 5.250 seconds (100% CPU, 400851 Lips)
尾递归版:
?- N is 2^20, length(L,N), maplist(=([]),L), time(mergeL(L,L,J)). % 2,097,152 inferences, 0.525 CPU in 0.526 seconds (100% CPU, 3997337 Lips)
这是10倍。
现在有更长的名单: 尾递归版:
?- N is 2^22, length(L,N), maplist(=([]),L), time(mergeL(L,L,J)). % 8,388,608 inferences, 4.228 CPU in 4.237 seconds (100% CPU, 1984272 Lips)
与原始订单:
?- N is 2^22, length(L,N), maplist(=([]),L), time(mergeL(L,L,J)). % 1,765,930 inferences, 1.029 CPU in 1.033 seconds (100% CPU, 1716119 Lips) ERROR: Out of local stack
因此只执行1.7Mi来填充本地堆栈。这主要是空间问题!如果你有比我更多的记忆,只需增加N is 2^22
!
答案 1 :(得分:1)
您可以添加剪辑:
mergeL([],[],[]) :- !.
mergeL(List, [], List):- !.
mergeL([], List, List):- !.
mergeL([X|Rest],[Y|Rest2], [XY|Res2]) :-
mergeL(Rest, Rest2, Res2),
append(X,Y,XY).
答案 2 :(得分:1)
第一个列表为空的查询,即?-merge([],,)将匹配第一个和第三个子句。同样,第二个列表为空的查询将匹配第一个和第二个子句。这就是为什么你得到选择点,因此重复解决方案。
如果你在第一个条款的正文中放置一个剪辑,你应该没问题:
merge([],[],[]) :- !.
答案 3 :(得分:1)
我会做出最后的选择:
mergeL([X|Rest],[Y|Rest2], [XY|Res2]) :-
mergeL(Rest, Rest2, Res2), !, append(X,Y,XY).
也@twinterer回答有效!