【问题标题】:Non-trivial Prolog find and replace非平凡的 Prolog 查找和替换
【发布时间】:2010-11-07 18:21:44
【问题描述】:

因此,我们可以通过以下方式在 Prolog 中轻松找到一个原子并将其替换为另一个原子:

replace([],A,B,[]).
replace([H|T],A,B,[B|Result]) :- 
    H=A, 
    replace(T,A,B,Result),!.
replace([H|T],A,B,[H|Result]) :- 
    replace(T,A,B,Result).

我相信还有其他方法可以做到这一点。

但是,我想在计算中做一些更复杂的逻辑。您将如何做一些事情,例如用 (x,y) 替换逻辑语句中的 conj(x,y) 之类的连词?所以它就像 final 和 replace 但不是原子。所以我们可以有类似reduce(conj(conj(x,y),z)). 的东西,我想减少到((x,y),z)

这是一个只有连词的简单示例,但这是我希望在连词的情况下发生的情况。如果有人感兴趣,这都是关于描述性逻辑和表格方法的。

当输入实际上不是列表时,我对如何进行查找和替换感到困惑;这是一个结构。如果不使用带有递归和列表的标准 [H|T] 技巧,我看不出如何解决这个问题。有人有什么想法吗?

非常感谢。

【问题讨论】:

  • 为什么不在第一个 H=A 之后而不是在 replace/4 之后放置剪切?您的代码将运行得更快,因为 Prolog 系统随后可以检测到 replace/4 是确定性的。

标签: prolog


【解决方案1】:

这是通过编写元解释器以简单的方式完成的,如下所示:

replace(V, V) :-
    % pass vars through 
    var(V), !.     
replace(A, A) :- 
    % pass atoms through 
    atomic(A), !.
replace([], []) :- 
    % pass empty lists through
    !.
replace([X|Xs], [Y|Ys]) :-
    % recursively enter non-empty lists
    !, 
    replace(X, Y),
    replace(Xs, Ys).
replace(conj(X,Y), (NX,NY)) :-
    % CUSTOM replacement clause for conj/2
    !, 
    replace(X, NX),
    replace(Y, NY).
replace(T, NT) :-
    % finally, recursively enter any as yet unmatched compound term
    T =.. [F|AL],
    replace(AL, NAL),
    NT =.. [F|NAL].

请注意倒数第二个子句,它用于替换您将conj/2 替换为连词,/2 的特定情况。通常,您可以以与此相同的方式添加尽可能多的其他子句来执行术语替换,因为此处定义的其余部分(replace/2 的所有其他子句)将递归解构 any PROLOG 术语,因为我们已经涵盖了所有类型;变量、原子和复合术语(包括明确的列表)。

在你的情况下执行这个给我们:

?- replace(conj(conj(x,y),z), NewTerm).
NewTerm = ((x, y), z).

请注意,此定义将正确替换嵌套在另一个术语中任意深度的任何术语。

【讨论】:

  • 谢谢你的鲨鱼。聪明的东西,但是......太聪明了,你推荐任何资源来学习元解释器吗?谢谢。
  • 太聪明了?作为一名 PROLOG 程序员,这是我经常用来进行术语操作的实用谓词,并且会出现在任何专业 PROLOG 程序员的技巧包中;)有关元解释器的资源,您可以通过以下方式查看“Prolog 中的编程” Clocksin 和 Mellish,以及 Leon Sterling 和 Ehud Shapiro 的“Prolog 的艺术”。 “元解释器”是指可以理解/解释 PROLOG 代码的 PROLOG 代码,它特别适合您的问题。
【解决方案2】:

回想一下,列表只是某种结构,因此您可以轻松地翻译代码以匹配任何其他结构。它可以帮助您为数据使用更清晰的表示:就像您使用 conj/2 来表示连词一样,您可以引入仿函数 var/1 来表示变量:

reduce(conj(X0,Y0), (X,Y)) :-
        reduce(X0, X),
        reduce(Y0, Y).
reduce(var(X), X).

例子:

?- reduce(conj(conj(var(x),var(y)), var(z)), R).
R = ((x, y), z).

【讨论】:

    【解决方案3】:

    您可以使用=.. 将通用术语转换为列表,例如

    ?- conj(conj(x,y),z) =.. List.
    List = [conj, conj(x, y), z].
    

    递归执行此操作,您可以展平整个术语。

    更改输入项中某些节点的一般谓词如下所示:

    change_term(NodeChanger, Term1, Term3) :-
        call(NodeChanger, Term1, Term2),
        change_term(NodeChanger, Term2, Term3),
        !.
    
    change_term(NodeChanger, Term1, Term2) :-
        Term1 =.. [Functor | SubTerms1],
        change_termlist(NodeChanger, SubTerms1, SubTerms2),
        Term2 =.. [Functor | SubTerms2].
    
    
    change_termlist(_, [], []).
    
    change_termlist(NodeChanger, [Term1 | Terms1], [Term2 | Terms2]) :-
        change_term(NodeChanger, Term1, Term2),
        change_termlist(NodeChanger, Terms1, Terms2).
    

    如果你现在定义:

    conj_changer(conj(X, Y), (X, Y)).
    

    然后您可以通过以下方式定义您的归约谓词:

    reduce(Term1, Term2) :-
        change_term(conj_changer, Term1, Term2).
    

    用法:

    ?- reduce(conj(conj(x,y),z), ReducedTerm).
    ReducedTerm = ((x, y), z).
    

    您必须小心定义NodeChanger 的方式,某些定义会导致change_term/3 循环。也许有人可以对此进行改进。

    【讨论】:

      猜你喜欢
      • 2012-09-09
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-03
      • 1970-01-01
      • 2012-10-17
      相关资源
      最近更新 更多