我尝试在prolog中实现一个非常简单的约束自由语法时遇到了无限递归问题。
这是我的规则:(vp - >动词短语,np - >名词短语,ap - > adj短语,pp - >预备短语)
verb(S) :- member(S, [put, pickup, stack, unstack]).
det(S) :- member(S, [the]).
adj(S) :- member(S, [big, small, green, red, yellow, blue]).
noun(S) :- member(S, [block, table]).
prep(S) :- member(S, [on, from]).
vp([V|R]) :- verb(V), pp(PP), np(NP), append(NP, PP, R).
np([D, N]) :- det(D), noun(N).
np([D|R]) :- det(D), ap(AP), noun(N), append(AP, [N], R).
ap([A]) :- adj(A).
ap([A|R]) :- adj(A), ap(R).
pp([P|R]) :- prep(P), np(R).
我遇到的问题是 ap 的规则会产生任意长的形容词串,所以在某些时候,我试图通过尝试所有这些无限的可能性来试图满足查询。
例如,以下查询将永远不会产生S = [put,the,red,block,on,the,green,block],因为它将首先将左侧“红色”上的形容词短语扩展为无限可能性尝试在右边。
?- vp(S)
S = [put, the, red, green, block, on, the, block] ;
答案 0 :(得分:5)
简短的回答是:使用Definite Clause Grammars(dcg)来表示你的语法。有关典型编码,请参阅this answer。
但是现在你的程序中存在实际问题。你不仅得不到想要的答案;情况更糟:即使在一个更简单的程序片段中,你也会遇到同样的问题。这是程序中仍然没有终止的最小片段:
verb(S) :- member(S, [put, pickup, stack, unstack]). det(S) :- member(S, [the]). adj(S) :- member(S, [big, small, green, red, yellow, blue]).noun(S) :- false, member(S, [block, table]). prep(S) :- member(S, [on, from]). vp([V|R]) :- verb(V), pp(PP), false,np(NP), append(NP, PP, R).np([D, N]) :- false, det(D), noun(N). np([D|R]) :- det(D), ap(AP), false,noun(N), append(AP, [N], R).ap([A]) :- false, adj(A). ap([A|R]) :- adj(A), ap(R), false. pp([P|R]) :- prep(P), np(R), false. ?- vp([put, the, red, green, block, on, the, block]).
通过插入目标 false
,我们得到了一个仍未终止的程序的小片段。
实际的源是ap/1
,它是递归的,但不受实际输入的限制。有关更多示例,请参阅failure-slice。
没有简单的方法来修复你的程序。最简单的方法是使用语法。
答案 1 :(得分:1)
好像你正在滥用Prolog的生成能力,将其追加到最后位置。我试图改变一个更明智的地方:
...
vp([V|R]) :- verb(V), append(NP, PP, R), pp(PP), np(NP).
np([D, N]) :- det(D), noun(N).
np([D|R]) :- det(D), append(AP, [N], R), ap(AP), noun(N).
...
现在你的解析器显然有用了。
?- vp([put, the, red, green, block, on, the, block]).
true .
但我建议,如已经假(+1),切换到DCG进行解析。
答案 2 :(得分:0)
基本问题是Prolog被定义为对规则执行DFS,因此当涉及无限搜索空间中的生成问题时(与您的情况一样),空间的某些部分可以不受影响。与平台无关的修复是使用深度限制来扩充语法,并在每次递归调用时递减深度。一旦深度达到0,则失败。通过重复查询(例如vp(S, 1). vp(S, 2).
)逐步增加深度,可以保证状态空间的所有部分最终都会被触及。
这基本上只是迭代加深。如果您使用的是SWI-PL,您还可以使用call_with_depth_limit/3
谓词完成相同的操作,而无需修改语法。