【问题标题】:Prolog: replacing an element in a list with another listProlog:用另一个列表替换列表中的元素
【发布时间】:2016-03-28 12:45:32
【问题描述】:

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

?- 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).

【问题讨论】:

  • 剪切应该在 concat/3 调用之后进行:concat(Repl, Temp, Res), !.
  • @CapelliC。不坚定! 如果我们要使用cut,那么我们最好这样做:repl([Val|T], Val, Repl, Res) :- !, concat(Repl, Temp, Res), repl(T, Val, Repl, Temp).
  • 请更具体!你对?- repl([x,y,x,y,x],x,[x,y,x],L). 有什么期望

标签: list replace prolog dcg prolog-dif


【解决方案1】:

要允许每个列表有多个匹配项,请使用 maplist/3 并按以下方式进行:

item_replacement_item_mapped(E, Es, E, Es)。 item_replacement_item_mapped(X, _, E, [E]) :- dif(X, E)。 复制(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.

【讨论】:

    【解决方案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.
    

    这是一种适用于所有方向的真实关系。它可以回答相当普遍的问题,例如:哪个项目被另一个列表替换了?示例:

    ?- 短语(替换([1,2,3],E,[2,3,4]),[1,2,2,3,4])。 E = 3 ; 错误的。

    【讨论】:

      【解决方案3】:

      编辑

      这越来越麻烦了,我的回答并没有准确地解释请求......所以让我们看看你的代码有 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 与众所周知的 append/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.
      

      编辑

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

      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).
      

      或者更好,扩展原来的like

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

      【讨论】:

      • 您能否告诉我们如果 Element 变成一个列表 (LElement) 并为 LElement 的每个元素重复“repl”谓词,应该添加哪些修改?谢谢
      • 您应该指定连续的 repl/4 是否对以前的结果进行操作每个都创建自己的副本。这两种行为中的哪一种?
      • 在以前的结果中运行。
      • 如何运行?- repl([x,y,x,y,x], x, [x,y,x], L).
      • 剪辑让你的程序失去了稳定性!因此,目标 repl([1, 2, 3], 3, [2, 3, 4], [1,2,3]) 成功了,即使它应该失败。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-20
      • 2011-08-16
      • 1970-01-01
      • 2013-03-21
      • 2021-05-19
      • 2015-08-17
      • 2018-02-19
      相关资源
      最近更新 更多