Prolog:用另一个列表替换列表中的元素

时间:2015-12-22 09:40:18

标签: list replace prolog dcg prolog-dif

对于以下查询(以及下面定义的谓词),我得到一个意外的答案:

?- rep([1,2,3], 3, [2,3,4], L).
L = [1, 2, 2, 3, 4] ;
L = [1, 2, 3].                           % unexpected answer

第一个结果是我想要的结果。第二个我不想要......

如何防止第二个?可能在某处添加!

concat([], L, L).
concat([H|T], L, [H|Res]) :-
   concat(T, L, Res).

repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :-
   repl(T, Val, Repl, Temp),
   concat(Repl, Temp, Res).
repl([H|T], Val, Repl, [H|Res]) :-
   repl(T, Val, Repl, Res).

3 个答案:

答案 0 :(得分:2)

要允许每个列表包含多个匹配项,请使用 maplist/3并按以下步骤操作:

item_replacement_item_mapped(E, Es, E, Es).
item_replacement_item_mapped(X,  _, E, [E]) :-
   dif(X, E).

repl(Es0,X,Xs,Es) :-
   maplist(item_replacement_item_mapped(X,Xs), Es0, Ess1),
   append(Ess1, Es).

示例查询:

?- repl([1,2,3], 3, [2,3,4], L).
   L = [1,2,2,3,4]
;  false.

?- repl([x,y,x,y,x], x, [x,y,x], L).
   L = [x,y,x,y,x,y,x,y,x,y,x]
;  false.

答案 1 :(得分:2)

由于@repeat已经很好地显示,你应该使用约束dif/2来描述两个术语是不同的。这避免了意外和错误的第二种解决方案。

此外,与描述列表时一样,还要考虑使用表示法:您可以使用非终结符list//1以声明方式描述列表,以便可以轻松高效地拼接到列表中特定职位的其他名单。

考虑:

replacement([], _, _) --> [].
replacement([L|Ls], L, Rs) -->
    list(Rs),
    replacement(Ls, L, Rs).
replacement([L|Ls], R, Rs) --> [L],
    { dif(L, R) },
    replacement(Ls, R, Rs).

list([]) --> [].
list([L|Ls]) --> [L], list(Ls).

我们使用接口谓词phrase/2来使用DCG。例如:

?- phrase(replacement([1,2,3], 3, [2,3,4]), Ls).
Ls = [1, 2, 2, 3, 4] ;
false.

这是一个真正的关系,可以在各个方向发挥作用。它可以回答相当普遍的问题,例如:哪个项目已被其他列表替换?示例:

?- phrase(replacement([1,2,3], E, [2,3,4]), [1,2,2,3,4]).
E = 3 ;
false.

答案 2 :(得分:0)

修改

这种情况变得多毛了,我的回答并没有准确地说明请求......所以让我们通过 minimal 更改看你的代码:

concat([], L, L).
concat([H|T], L, [H|Res]) :-
   concat(T, L, Res).

repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :- !, % as noted by @repeat, better to commit early...
   repl(T, Val, Repl, Temp),
   concat(Repl, Temp, Res). % !.
repl([H|T], Val, Repl, [H|Res]) :-
   repl(T, Val, Repl, Res).

剪切只是提交第二个条款......

恢复旧答案

你的concat / 3与众所周知的追加/ 3相同,所以请考虑这种方法

repl(ListOrig, Element, Replace, ListUpdated) :-
      append(H, [Element|T], ListOrig),
      append(H, Replace, Temp),
      append(Temp, T, ListUpdated).

?- repl([1, 2, 3], 3, [2, 3, 4], L).
L = [1, 2, 2, 3, 4] ;
false.

修改

根据评论的要求,此扩展处理要匹配更改的元素列表,使用简单的模式匹配(注意:在前一个子句之前添加)

repl(ListOrig, [], _Replace, ListOrig).
repl(ListOrig, [E|Es], Replace, ListUpdated) :-
    repl(ListOrig, E, Replace, Temp),
    repl(Temp, Es, Replace, ListUpdated).

测试

?- repl([1,2,3],[2,3],[x,y,z],R).
R = [1, x, y, z, x, y, z] ;
false.

修改

我没有注意到如果找不到Element就不应该失败...... 最后一个' catchall'子句可以处理这种情况:

repl(ListOrig, _Element, _Replace, ListOrig).

或更好,扩展原始

repl(ListOrig, Element, Replace, ListUpdated) :-
      (  append(H, [Element|T], ListOrig)
      -> append(H, Replace, Temp),
         append(Temp, T, ListUpdated)
      ;  ListOrig = ListUpdated
      ).