DCG和左递归

时间:2012-11-21 23:53:06

标签: prolog dcg left-recursion failure-slice

我正在尝试实现一个带有一组{a,b,c,d} *形式的字符串的dcg。我遇到的问题是如果我有一个形式s的查询([a,c, b],[]),它返回true,这是正确的答案,但是当我有一个形式s([a,c,f],[])的查询时,它不会返回一个答案,它用完本地叠加。

s --> [].
s --> s,num.
num --> [a].
num--> [b].
num--> [c].
num--> [d].

2 个答案:

答案 0 :(得分:8)

使用phrase/2

让我们尝试phrase(s,[a,b,c])代替s([a,b,c],[])。原因很简单:通过这种方式,我们明确表示我们使用的是DCG(),而不是普通的谓词。 phrase/2是语法的“官方”界面。

所以你的第一个问题是为什么phrase(s,[a,c,f])没有终止而phrase(s,[a,b,c])“给出了正确答案” - 如你所说。现在,这很快回答:两者都不会终止!但是phrase(s,[a,b,c])找到了解决方案/答案。

通用终止

要区分两件事:如果您输入查询并得到trueX = a等答案;您可能有兴趣获得更多。通常通过在顶层输入 SPACE ; ENTER 来完成此操作。因此,查询可能仅在找到第一个或多个答案后才开始循环。随着时间的推移,这变得非常混乱:你是否应该永远记住这个谓词可能会产生答案;另一个谓词产生两个,后来才会循环?

最简单的方法是建立通用终止的概念,这是最强大的概念。如果Goal终止,Goal, false将终止。此false目标对应于无限期地点击 SPACE ;直到整个查询失败的那一刻。

所以现在尝试:

?- phrase(s,[a,c,f]), false.
** LOOPS **

但是:

?- phrase(s,[a,b,c]), false.
** LOOPS **

从通用终止的角度来看,两个查询都不会终止。在最频繁使用的字词中,终止等同于通用终止。找到答案或解决方案就是这样,但没有任何终止。因此,只要您对答案感到满意但基本上不会终止,那么查询看起来就是无害的。但是很高兴您能够如此迅速地发现这一点:如果您只是在正在运行的应用程序中发现这一点会更糟糕。

确定原因

下一步让我们确定不终止的原因。您可能会尝试调试器或跟踪器,但很可能它根本不会给您一个很好的解释。但是有一个更简单的方法:使用。只需在您的语法中添加非终端{false};和目标false进入谓词。我们可以在这里利用一个非常漂亮的财产:

  

如果,故障切片未终止,则原始程序不会终止。

所以,如果我们很幸运并且找到了这样的切片,那么我们肯定知道只有在剩余的可见部分被改变某种程度上时才会终止。最有用的切片是:

?- phrase(s,[a,b,c]), false

s --> [], {false}.
s --> s, {false}, num.

你的程序还剩下多少东西! num//0已经过去了!没有人关心num//0。这意味着:num//0可以描述任何,无论如何 - 程序仍会循环。

要解决问题,我们必须更改可见部分中的内容。剩下的不多了!正如您已经观察到的,我们在这里有一个左递归。修复它的经典方法是:

重新制定语法

您可以轻松地将语法重新表达为正确的递归:

s --> [].
s --> num, s.

现在两个查询都终止了。这是编译器构造中也称为的经典方法。

但有些情况下,语法的重新制定是不合适的。这个简单的例子不属于这种情况,但它常常出现在语法中,并且有些模糊不清。在这种情况下,你仍然可以:

添加终止诱导参数

?- Xs = [a,b,c], phrase(s(Xs,[]), Xs).

s(Xs,Xs) --> [].
s([_|Xs0],Xs) --> s(Xs0,Xs1), num, {Xs1=Xs}.

固有的非终止查询

无论您做什么,请记住并非每个查询都可以终止。如果你问:»告诉我所有存在的自然数 - 真的全部,一个一个!«然后回答这个的唯一方法是从0开始并计算它们。所以有一些问题,其中有无穷无尽的答案/解决方案,我们不能责怪可怜的Prolog实现我们的愿望。但是,在这种情况下我们最喜欢的是以公平的方式列举所有解决方案。我们可以通过具有良好终止特性的语法来做到这一点;也就是说,一个语法终止于固定长度列表。像这样:

?- length(Xs, M), phrase(s, Xs).

有关如何应用失败切片的其他示例,请参阅标记

答案 1 :(得分:0)

我不知道这是否有任何帮助,因为我正在使用的prolog似乎有一个非常不同的语法,但我只是编写了以下程序来尝试和你的匹配,它工作正常。

<强>程序

s([]).
s([X|Xs]) :- num(X), s(Xs).

num(a).
num(b).
num(c).
num(d).

<强>输出

?- [prologdcg].
% prologdcg compiled 0.00 sec, 2,480 bytes
true.

?- s([a,c,b]).
true.

?- s([a,c,f]).
false.

使用SWI-prolog运行。