Prolog - 普通规则的性能是否优于列表?

时间:2012-05-21 10:49:44

标签: performance prolog dcg

我有一套DCG规则(在这种情况下是德语人称代词):

% personal pronoun (person, case, number, genus)
ppers(1,0,sg,_) --> [ich].
ppers(1,1,sg,_) --> [meiner].
ppers(1,2,sg,_) --> [mir].
ppers(1,3,sg,_) --> [mich].
ppers(2,0,sg,_) --> [du].
ppers(2,1,sg,_) --> [deiner].
ppers(2,2,sg,_) --> [dir].
ppers(2,3,sg,_) --> [dich].
...

因为它们是语义连接的,所以通过将这些信息移动到列表(例如按人员分组)而不是不相关的规则来保留这些信息是有意义的。这也使事情变得更加简洁:

ppers(1,sg,_,[ich, meiner, mir, mich]).
ppers(2,sg,_,[du,deiner,dir,dich]).
...

然后我会用nth0()选择我想要的项目,其中我需要的是列表中的索引。

但是,我在通过程序进行追踪时注意到,当检查德语句子的正确语法并试图查找某个部分是否是人称代词时,Prolog将不会在我使用上部版本时逐步完成每个实例(普通规则) ),但是当我使用下面的列表版本时,它将遍历每个列表。

如果我使用list和nth0与普通规则相比,这是否意味着性能会更差?或者,Prolog跟踪器是不是像列表那样显示对普通规则的爬行?

(我希望我可以让我的问题显而易见,如果不是我会扩展。)

4 个答案:

答案 0 :(得分:2)

最有可能的速度和跟踪差异不是由索引(*)引起的,而是由子句统一和主体调用nth之间的速度和跟踪差异引起的。如果你真的想利用索引并希望在大多数Prolog系统中都可以移植(**),那么你需要为第一个参数索引重新设计你的问题。

实现此目的的一种方法是通过附加谓词。假设您最初有这些DCG规则:

cat(Attr1, .., Attrn) --> [Terminal1, .., Terminaln].
..

将其转换为:

cat(X1, .., Xn) --> [Y], cat2(Y, X1, .., Xn).

cat2(Terminal1, Attr1, .., Attrn) --> [Terminal2, .., Terminaln].
..

当我们将此应用于您的示例时,我们会得到:

% personal pronoun (person, case, number, genus)
ppers(X1,X2,X3,X4) --> [Y], ppers2(Y,X1,X2,X3,X4).

% personal pronoun 2 (first word, person, case, number, genus)
ppers2(ich,1,0,sg,_) --> [].
ppers2(meiner,1,1,sg,_) --> [].
ppers2(mir,1,2,sg,_) --> [].
ppers2(mich,1,3,sg,_) --> [].
ppers2(du,2,0,sg,_) --> [].
ppers2(deiner,2,1,sg,_) --> [].
ppers2(dir,2,2,sg,_) --> [].
ppers2(dich,2,3,sg,_) --> [].

您可以为代码中的每个类别执行此操作 一种词典表。以上工作独立于DCG的方式 翻译,如果存在第一个参数索引,它将是 闪电般快速。

再见

(*) 即使您的Prolog系统可以进行多参数索引,它仍可能不会执行复杂的术语索引。要索引一个[ich | X],Prolog系统需要下降到列表中,但很可能它不会下降并且仅索引(。)/ 2,因此所有子句看起来都相同而索引没有正面效果。

(**) 我想Prolog系统中唯一关注索引的共同点是第一个参数索引。除此之外,并非所有Prolog系统都可能将终端设置在头部。有些人可能在身体中使用= / 2而有些可能在身体中使用'C'/ 3。 DCG目前尚未标准化与终端建模有关的内容。

答案 1 :(得分:1)

一般情况下,跟踪器会告诉你实际发生了什么,所以是的,如果它迭代了替代公式通过匹配直接访问目标术语的位置,那么当你不看时也会发生这种情况。但要了解这实际上是否意味着性能更差,您来衡量和比较真实场景中的两种选择。统一可能会很慢,即使它没有被跟踪器显示为单独的步骤,或者您的运行时系统可能会进行优化甚至编译在trace下不会发生的事情。或者它可能更慢但不足以担心。在这里,一如既往,黄金法则是:措施,然后优化。

答案 2 :(得分:1)

你为什么使用nth0?也许可能是性能杀手的罪魁祸首,而是使用memberchk。

除此之外,我认为你对表演的直觉在“论证索引”中有着良好的背景。 DCG通常在Prolog中翻译(我在这里使用SWI-Prolog):

ppers(1,0,sg,_) --> [ich].

变为

ppers(1, 0, sg, _, [ich|A], A).

最近对SWI-Prolog虚拟引擎进行了一次优化,灵感(我认为)来自YAP,它自动构建了具有足够绑定参数的谓词的所有索引。

因此,您可以期望使用第一个方案解析(使用SWI-Prolog)将更有效。

以前,只有'第一个参数索引'被实现,在这种情况下(或者如果你使用Prolog而没有索引功能),你应该在这些方案之间找到非常相似的时间。

HTH

答案 3 :(得分:0)

语法规则被编译成谓词子句,通常是索引子句。大多数(如果不是全部)Prolog编译器使用第一个参数索引(默认情况下)来避免尝试在证明目标时永远不会成为证明树一部分的子句。因此,根据您的调用模式,以及您使用Prolog编译器跟踪支持观察到的情况,不会进入每个谓词子句。此外,使用实例化索引调用nth0 / 3谓词仍然需要对列表进行线性遍历,直到达到指定的索引。如果您使用memberchk / 2谓词,则与其他人建议的相同。列表是列表。

相关问题