通常的香草口译员使用Prolog回溯 本身进行存档回传。我想这就是原因 为什么叫“香草”:
solve(true).
solve((A,B)) :- solve(A), solve(B).
solve(H) :- clause(H, B), solve(B).
不使用任何语言的“辣椒”解释器怎么样 Prolog回溯。基本上是谓词 first /?以获得 第一个解决方案和谓词 next /?以获得进一步的解决方案。
人们将如何在Prolog中实现这样的口译员。解决方案不必是纯粹的,也可以使用findall和cut。尽管更纯净的解决方案也可以说明问题。
答案 0 :(得分:2)
此解决方案是Markus Triska的A Couple of Meta-interpreters in Prolog(序言的力量的一部分)中的回溯还原中提供的解释器的简化版本。它比该代码更冗长,效率更低,但可能比该代码更容易理解。
first(Goal, Answer, Choices) :-
body_append(Goal, [], Goals),
next([Goals-Goal], Answer, Choices).
next([Goals-Query|Choices0], Answer, Choices) :-
next(Goals, Query, Answer, Choices0, Choices).
next([], Answer, Answer, Choices, Choices).
next([Goal|Goals0], Query, Answer, Choices0, Choices) :-
findall(Goals-Query, clause_append(Goal, Goals0, Goals), Choices1),
append(Choices1, Choices0, Choices2),
next(Choices2, Answer, Choices).
clause_append(Goal, Goals0, Goals) :-
clause(Goal, Body),
body_append(Body, Goals0, Goals).
body_append((A, B), List0, List) :-
!,
body_append(B, List0, List1),
body_append(A, List1, List).
body_append(true, List, List) :-
!.
body_append(A, As, [A|As]).
这个想法是,Prolog引擎状态表示为析取Choices
的列表,起到选择点堆栈的作用。每个选择的格式为Goals-Query
,其中Goals
是仍有待满足的目标的联合列表,即SLD树的该节点上的解析器,而Query
是原始查询字词,其变量已根据到达该节点的路径中的统一实例化。
如果某个选项的解析器为空,则其Query
实例化将作为一个Answer
返回,我们将继续其他选项。请注意,当找不到目标的子句时,即“失败”,Choices1
与[]
结合在一起,我们通过进行Choices0
中的其余选择来“回溯”。还要注意,当列表中没有选择时,next/3
将失败。
示例会话:
?- assertz(mem(X, [X|_])), assertz(mem(X, [_|Xs]) :- mem(X, Xs)).
true.
?- first(mem(X, [1, 2, 3]), A0, S0), next(S0, A1, S1), next(S1, A2, S2).
A0 = mem(1, [1, 2, 3]),
S0 = [[mem(_G507, [2, 3])]-mem(_G507, [1, 2, 3])],
A1 = mem(2, [1, 2, 3]),
S1 = [[mem(_G579, [3])]-mem(_G579, [1, 2, 3])],
A2 = mem(3, [1, 2, 3]),
S2 = [[mem(_G651, [])]-mem(_G651, [1, 2, 3])].
此方法的问题在于findall/3
进行了很多分解体的复制,即要在分离分支中证明的目标的其余结合。我希望看到一个更有效的解决方案,在该解决方案中可以复制术语并更巧妙地共享变量。
答案 1 :(得分:1)
这是使用差异列表进行的精确回溯的一种变体。
first(G, [[]|L], R) :- !, first(G, L, R). %% choice point elimination
first([A], L, [A|L]) :- !.
first([H|T], L, R) :- findall(B, rule(H,B,T), [B|C]), !, first(B, [C|L], R).
first(_, L, R) :- next(L, R).
next([[B|C]|L], R) :- !, first(B, [C|L], R).
next([_|L], R) :- next(L, R).
通过差异列表表示规则和事实的过程需要使用Peano算术,如下所示:
rule(add(n,X,X),T,T).
rule(add(s(X),Y,s(Z)),[add(X,Y,Z)|T],T).
rule(mul(n,_,n),T,T).
rule(mul(s(X),Y,Z),[mul(X,Y,H),add(Y,H,Z)|T],T).
您可以按以下方式运行查询:
?- first([mul(s(s(n)),s(s(s(n))),X),X],[],[X|L]).
X = s(s(s(s(s(s(n))))))
L = []
?- first([add(X,Y,s(s(s(n)))),X-Y],[],[X-Y|L]).
X = n
Y = s(s(s(n)))
L = [[[add(_A,_B,s(s(n))),s(_A)-_B]]]
?- first([add(X,Y,s(s(s(n)))),X-Y],[],[_|L]), next(L,[X-Y|R]).
L = [[[add(_A,_B,s(s(n))),s(_A)-_B]]],
X = s(n)
Y = s(s(n))
R = [[[add(_C,_D,s(n)),s(s(_C))-_D]]]