dcg 使用了一个叫做差异列表的概念。假设你想要进行解析(你也可以使用这些谓词生成列表,但暂时不考虑这点)。
如果你解析一个列表,比如[the,man,kisses,the,woman]
。你可以把它看作是一个单词火车,我从@Vikramnath Venkatasubramani那里“借”了这个比喻,所以感谢他/她。现在如果我们调用s([the,man,kisses,the,woman],C)
,C
会返回 []
。
每个谓词都会断开零个、一个或多个车厢。因此,在以下情况下:
s(A,C) :-
np(A,B),
vp(B,C).
np/2
将断开车厢[the,man]
,导致它仍然存储剩余的火车B=[kisses,the,woman]
。现在vp/2
将断开所有剩余的车厢,结果是C=[]
,一个空火车。
这是如何实现的
让我们考虑语法的部分实现。
s(A,C) :-
np(A,B),
vp(B,C).
np(A,B) :-
det(A,D),
n(D,B).
vp(B,C) :-
v(B,E),
np(E,C).
vp(B,C) :-
v(B,C).
det([the|W],W).
det([a|W],W).
n([woman|W],W).
n([man|W],W).
v([kisses|W],W).
正如之前所说的,你调用np([the,man,kisses,the,woman],B)
,np/2
将必须断开形成名词短语的车厢:[the,man]
。
np/2
接着调用det/2
和n/2
。现在det/2
将断开决定词:the
,而n/2
将断开名词man
,以使其更加明确:
np([the,man,kisses,the,woman],[kisses,the,woman]) :-
det([the,man,kisses,the,woman],[man,kisses,the,woman]),
n([man,kisses,the,woman],[kisses,the,woman]).
现在,
det/2
不再转移其职责,它被实现为:
det([the|W],W).
现在,如果我们进行模式匹配,这将会归结为:
det([the,man,kisses,the,woman],[man,kisses,the,woman]).
这意味着它已经断开了
the
。
使用这种方法的优点是,断开连接可以在常量时间内完成。实际上,谓词不知道列表的整个尾部。
此外,它还允许在事实中断开多个单词。例如,假设您将自己的姓名添加为名词:
n([s,dallapalma|W],W).
在这种情况下,
n/2
将同时断开两节车厢。其他谓词不需要意识到这一点,例如
s/2
不必决定它在哪个位置将火车分割成
np/2
和
vp/2
之间的部分:它让
np/2
断开它想要的所有车厢,并且
vp/2
将努力处理火车的其余部分。
'C'(A,the,B)
?当我尝试时,你原始DCG源代码的列表中并没有显示det/2
的定义。但是直接回答这个问题,'C'
是一个原子,所以'C'(A,the,B)
是对名为'C'
的谓词进行调用,它有三个参数:A
(变量),the
(原子)和B
(变量)。s(A,C)
有两个参数,因为Prolog在内部添加了一个差分列表参数,这是它实现DCG的方式。通常使用phrase(s, L)
调用DCG。 - lurker