Prolog - 替换列表元素

3

我有一个程序,其中有一个谓词用于将列表中某个元素的第一次出现替换为给定的新元素,并生成一个新列表。我的实现方式如下:

    changeFst(OldE,[OldE|T],NewE,[NewE|T]):-!.
    changeFst(OldE,[_|T],NewE,_):- changeFst(OldE,T,NewE,_),!.

例如,如果你输入(2,[1,2,3,4,2],10,X),它会返回X=[1,10,3,4,2]。现在我正在编写更改最后一个出现位置的代码(在示例中,它将返回X=[1,2,3,4,10])。以下是我的代码:
    changeLast(OldE,OldL,NewE,NewL):-
       reverse(OldE,X), 
       changeFst(OldE,X,NewE,NewL),
       !.

这段代码实际上可以完美运行,但问题在于它返回了一个反转后的列表(在我的示例中,它返回了[10,4,3,2,1]而不是[1,2,3,4,10]

我该如何将其再次反转以正确显示结果?


您的定义不正确:changeFst(o,[o,o],n,[o,n]). 成功了,但根据您的描述,它应该失败! - false
即使 changeFst(o,[o,o],n,[]). 成功了,但显然它应该失败。 - false
2个回答

4
您对于changeFst/4的定义存在很多错误,例如changeFst(o,[o,o],n,[m,y,s,t,e,r,y]).可以成功,但显然应该失败。原因是您错误地使用了割符(cuts)。如果您想学习Prolog,请先坚持纯声明性子集。这意味着没有割符,也没有副作用。

因此,这里提供一个不依赖割符的定义:

changeFst(Old,[Old|Olds],New,[New|Olds]).
changeFst(Old,[E|Olds],New,[E|News]):-
   dif(Old, E),
   changeFst(Old,Olds,New,News).

这种纯关系的一个优点是,我们可以使用最通用的查询来查看我们得到的答案:
?- changeFst(Old, Olds, New, News).
   Olds = [Old|_A], News = [New|_A]
;  Olds = [_A,Old|_B], News = [_A,New|_B], dif(Old,_A)
;  Olds = [_A,_B,Old|_C], News = [_A,_B,New|_C],
   dif(Old,_A), dif(Old,_B)
;  Olds = [_A,_B,_C,Old|_D], News = [_A,_B,_C,New|_D],
   dif(Old,_A), dif(Old,_B), dif(Old,_C)
; ... .

您是否注意到答案中始终包含Olds部分列表?例如第一个答案中的Olds = [Old|_A]。这可能有点太笼统了,毕竟它意味着甚至非列表现在也被接受:

?- changeFst(o,[o|nonlist], New, News).
   News = [New|nonlist]
;  false.

所以你可能希望确保OldsNews始终为列表。

但我想要表明的是,通过纯关系,您可以看到许多直接受到削减程序限制的程序无法向您显示的内容。

如果我们在这里:空列表该怎么办?当前版本建议changeFst/4应该失败。不确定你想要什么,但如果你想让它成功,请先添加一个事实changeFst(_,[],_,[]).

请参见this answer以获取dif/2的定义(如果您的Prolog不支持)。


1
保持纯净高效,可以像@false建议的那样使用if_/3(=)/3
changeFst(Old,Olds,New,News) :-
   list_change_first_(Olds,News,Old,New).

list_change_first_([],[],_,_).
list_change_first_([X|Xs],[Y|Ys],Old,New) :-
   if_(X = Old, (Y = New, Ys = Xs),
                (Y = X,   list_change_first_(Xs,Ys,Old,New))).

示例查询:

?- changeFst(2,[1,2,3,4,2],10,Xs).
Xs = [1,10,3,4,2].                         % succeeds deterministically

?- changeFst(o,[o,o],n,[m,y,s,t,e,r,y]).
false.                                     % expected result

?- changeFst(Old,Olds,New,News).
Olds = [],             News = []                                       ;
Olds = [Old|_A],       News = [New|_A]                                 ;
Olds = [_A],           News = [_A],           dif(_A,Old)              ;
Olds = [_A,Old|_B],    News = [_A,New|_B],    dif(_A,Old)              ;
Olds = [_A,_B],        News = [_A,_B],        dif(_A,Old), dif(_B,Old) ;
Olds = [_A,_B,Old|_C], News = [_A,_B,New|_C], dif(_A,Old), dif(_B,Old) % and so on...

“% 等等……”这个名字不太好听,你有更好的名字吗?理想情况下,[tag:prolog-toplevel] 可以使用这样的约定来表示查询已停止。我考虑过 ...,但不是100%确定。 - false
@false。使用SO键盘标记如何?我有时看到你在使用它们...有没有好的使用建议?例如,“Key ;”与“key enter”相比,或者根本没有按键但引发了异常。 - repeat
最好是有效的Prolog语法,ASCII格式,并由toplevel打印。 - false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接