Prolog语法规则与逻辑关系解析
1. 语言到逻辑的转换
在处理语言时,我们常常需要将其转换为逻辑形式以便进行分析。例如,对于句子“Eat your supper”,我们可以插入额外的词使其变为 “You eat your supper”,这样就具有了名词短语/动词短语的结构,符合我们对句子结构的认知。我们可以通过以下部分语法规则来实现这种转换:
sentence --> imperative, noun_phrase, verb_phrase.
imperative, [you] --> [].
imperative --> [].
其中,第一条关于祈使句的规则实际上可转换为
imperative(L, [you|L])
,这意味着返回的序列会比原序列更长。一般来说,语法规则的左侧可以由一个非终结符和一个单词列表用逗号分隔,其含义是在解析时,在右侧的目标从输入序列中消耗完单词后,将这些单词插入到输入序列中。
练习 9.3
即使给定的语法规则定义完整,以一系列标记作为输入时,它也不能构成一个有用的解析器。这是因为语法规则只是描述了句子的结构,但没有考虑到语义和上下文信息,无法准确地对输入的句子进行解析和理解。
英语句子到谓词演算的转换
为了展示确定子句文法(DCGs)如何用于对语言进行更复杂的分析,我们给出一个示例,将(有限数量的)英语句子直接转换为谓词演算中表示其含义的形式。以下是相关的语法规则:
?- op(500,xfy,&).
?- op(600,xfy,->).
sentence(P) -->
noun_phrase(X, PI, P), verb_phrase(X, PI).
noun_phrase(X, Pi, P) -->
determiner(X, P2, Pi, P),
noun(X, P3),
rel_clause(X, P3, P2).
noun_phrase(X, P, P) --> proper_noun(X).
verb_phrase(X, P) -->
trans_verb(X, Y, PI), noun_phrase(Y, PI, P).
verb_phrase(X, P) --> intrans_verb(X, P).
rel_clause(X, PI, (P1&P2)) -->
[that], verb_phrase(X, P2).
rel_clause(_, P, P) --> [].
determiner(X, PI, P2, all(X,(Pl -> P2))) --> [every].
determiner(X, PI, P2, exists(X, (P1&P2))) --> [a].
noun(X, man(X)) --> [man].
noun(X, woman(X)) --> [woman].
proper_noun(john) --> [john].
trans_verb(X, Y, loves(X,Y)) --> [loves].
intrans_verb(X, lives(X)) --> [lives].
在这个程序中,参数用于构建表示短语含义的结构。对于每个短语,最后一个参数实际上指定了该短语的含义。然而,短语的含义可能取决于其他几个因素,这些因素由其他参数给出。例如,动词 “lives” 会产生形式为
lives(X)
的命题,其中
X
代表生活的人。“lives” 的含义无法预先确定
X
是什么,它必须应用于某个特定对象才有用,动词使用的上下文将决定这个对象是什么。像 “every” 这样的词则更为复杂,其含义必须应用于一个变量和两个包含该变量的命题,结果表示如果在第一个命题中用一个对象替换变量得到真,那么在第二个命题中用同一个对象替换该变量也会得到真。
练习 9.4
我们需要阅读并理解这个程序,尝试运行它,给出类似
?- sentence(X, [every,man,loves,a,woman],[]).
的目标。对于句子 “every man that lives loves a woman” 和 “every man that loves a woman lives”,程序会生成相应的逻辑含义。句子 “Every man loves a woman” 实际上是有歧义的,可能存在一个所有男人都爱的女人,也可能每个男人都爱一个(可能不同的)女人。该程序是否会将这两种可能的含义作为替代解决方案输出呢?如果不会,原因是程序在构建句子含义时做了简单假设,即默认按照一种固定的逻辑结构来处理,没有考虑到所有可能的语义解释。
2. 语法规则的更广泛应用
语法规则符号可以更广泛地用于隐藏作为累加器或差异结构的额外一对参数。除了处理终结符(列表中的实际单词)外,语法规则翻译机制添加的这两个额外参数可用于跟踪在Prolog计算过程中发生变化的任何单一信息。例如,对于规则
noun_phrase(X, Y) :- determiner(X, I), noun(Z, Y).
,更中立的解读是:如果限定词在情况
X
下为真,并且在之后的情况(
Z
)下名词为真,那么名词短语在由
X
表征的情况下为真,名词短语之后的情况与名词之后的情况相同。对于语法规则,“情况” 通常是待处理的单词列表,但也有其他可能性。
语法规则中终结符的出现标志着从一种 “情况” 到另一种 “情况” 的转变。在通常使用语法规则时,这是从有一些未处理的单词列表到该列表去掉第一个单词的转变。所有 “情况” 的变化最终都归结为这种类型的变化序列(我们遍历单词列表的唯一方法是反复找到语法规则中指定的终结符)。
为了推广语法规则的使用,定义一个谓词来描述终结符如何从一种情况过渡到另一种情况是很有用的。通过为这个谓词提供不同的定义,我们可以让语法规则按常规方式执行,也可以让它们做不同的事情。这个谓词通常称为
'C'/3
(注意由于
C
是大写,需要加引号),对于正常语法规则,其定义如下:
% 'C'(Prev, Terminal, New)
% 若终结符 Terminal 导致从情况 Prev 过渡到情况 New,则成功
'C'([W|Ws], W, Ws).
也就是说,如果情况(未使用的单词列表)是
[W|Ws]
,并且终结符
W
被指定为当前语法规则中的下一个元素,我们就可以过渡到未使用单词列表为
Ws
的新情况。
在转换为Prolog时,语法规则中的终结符可以用
'C'
来表示,而不是直接用它们对列表参数的含义来表示。实际上,许多Prolog系统都是用
'C'
来翻译语法规则的。例如,对于
determiner --> [the].
,翻译为
determiner(In,Out) :- 'C'(In,the,Out).
,而不是
determine([the|S],S).
。根据上面给出的
'C'
的定义,这两个Prolog定义对于
determiner
总是产生完全相同的答案。所以实际上当你正常使用语法规则时,不需要知道使用了哪种翻译方法。
计算列表长度
通过修改
'C'/3
的定义,语法规则可以用于记录除了不断缩小的列表之外的其他信息。例如,在计算列表长度时,我们可以记录到目前为止遇到的项目数量。以下是使用语法规则重新表达的代码:
listlen(L, N) :- lenacc(L, 0, N).
lenacc([]) --> [].
lenacc([H|T]) --> [1], lenacc(T).
在这种情况下,遇到终结符
1
会使到目前为止的总数加
1
。所以我们需要的
'C'
的定义是:
'C'(Old, X, New) :- New is Old + X.
计算零件列表
另一个例子是将计算零件列表的过程重写为维护到目前为止找到的零件的递增列表。以下是使用语法规则的代码示例:
partsof(X, P) :- partsacc(X, [], P).
partsacc(X) --> [X], {basicpart(X)}.
partsacc(X) --> {assembly(X SubParts)}, partsacclist(Subparts).
partsacclist([]) --> [].
partsacclist([P|Tail]) --> partsacc(P), partsacclist(Tail).
在这种情况下,
'C'
的适当定义是:
'C'(Old, X, [X|Old]).
重要注意事项
phrase/2
谓词假设我们感兴趣的是计算过程中表示的最终情况为
[]
。如果你更改了
'C'
的定义,可能需要定义自己修改后的
phrase/2
版本,使其不做这个假设。
3. Prolog与逻辑的关系
Prolog是由Alain Colmerauer及其同事在20世纪70年代左右发明的编程语言,它是设计一种实用编程语言的首次尝试,使程序员能够用逻辑而不是传统的编程结构来指定任务。“Prolog” 这个名字的含义就是 “Programming in Logic”。
谓词演算简介
如果我们要讨论Prolog与逻辑的关系,首先需要明确逻辑的含义。逻辑最初是作为表示论证形式的一种方式而设计的,这样就可以用形式化的方法检查论证是否有效。我们可以用逻辑来表达命题、命题之间的关系以及如何从其他命题有效推断出某些命题。这里我们要讨论的特定逻辑形式是谓词演算。
在谓词演算中,我们用项来表示对象,项有以下几种形式:
-
常量符号
:代表单个个体或概念的符号,例如
greek
、
agatha
和
peace
,可以看作Prolog原子。
-
变量符号
:在不同时间可能代表不同个体的符号,通常与量词一起引入,例如
X
、
Man
和
Greek
,可以看作Prolog变量。
-
复合项
:由一个函数符号和一组有序的项作为参数组成,代表一个依赖于参数所代表个体的个体。例如,
wife(henry)
可能表示亨利的妻子,
distance(pointl, X)
可能表示某个特定点与某个待指定位置之间的距离,
classes(mary, dayafter(W))
可能表示玛丽在某一天
W
的后一天所教的课程。可以将复合项看作以函数符号为函子的Prolog结构。
为了表达关于对象的命题,我们需要表达对象之间的关系,这通过谓词符号来实现。原子命题由一个谓词符号和一组有序的项作为参数组成,类似于Prolog目标。例如,
human(mary)
、
likes(man, wine)
和
owns(X, donkey(X))
都是原子命题。在Prolog中,一个结构可以作为目标、另一个结构的参数或两者兼而有之,但在谓词演算中,函数符号(用于构造参数)和谓词符号(用于构造命题)有严格的区分。
我们可以通过多种方式从原子命题构造复合命题,这里开始出现一些在Prolog中没有直接对应物的内容。我们可以使用逻辑连接词来构造更复杂的命题,以下是逻辑连接词的总结:
| 连接词 | 传统谓词演算(PC)语法 | 计算机语法 | 含义 |
| ---- | ---- | ---- | ---- |
| 否定 |
¬α
|
~α
| “非α” |
| 合取 |
α ∧ β
|
α & β
| “α 且 β” |
| 析取 |
α ∨ β
|
α ∨ β
| “α 或 β” |
| 蕴含 |
α ⊃ β
|
α -> β
| “α 蕴含 β” |
| 等价 |
α ≡ β
|
α <-> β
| “α 等价于 β” |
此外,
α -> β
等价于
(~α) ∨ β
,
α <-> β
等价于
(α & β) ∨ (~α & ~β)
,也等价于
(α -> β) & (β -> α)
。
在命题中出现变量时,其含义只有在通过量词引入这些变量时才被定义。谓词演算提供了两个量词:
| 量词 | PC语法 | 计算机语法 | 含义 |
| ---- | ---- | ---- | ---- |
| 全称量词 |
∀v.P
|
all(v,P)
| “无论 v 代表什么,P 都为真” |
| 存在量词 |
∃v.P
|
exists(v,P)
| “存在 v 可以代表的事物,使得 P 为真” |
例如,
all(X, man(X) -> human(X))
表示无论选择什么
X
,如果
X
是男人,那么
X
就是人类;
exists(Z, father(john, Z) & female(Z))
表示存在一个
Z
,使得约翰是
Z
的父亲且
Z
是女性,即约翰有一个女儿。
子句形式
谓词演算中用
->
(蕴含)和
<->
(等价)表示的公式可以重写为用
&
(合取)、
∨
(析取)和
~
(否定)表示的形式。由于存在冗余,同一命题有多种写法,这在对谓词演算公式进行形式操作时非常不便。因此,我们考虑将谓词演算命题转换为一种特殊形式——子句形式,在这种形式下表达同一事物的方式更少。实际上,子句形式的谓词演算命题非常像一组Prolog子句,所以研究子句形式对于理解Prolog与逻辑的关系至关重要。
将谓词演算公式转换为范式有六个主要阶段:
1.
去除蕴含
:根据定义替换
->
和
<->
的出现。例如,
all(X, man(X) -> human(X))
会转换为
all(X, ~man(X) ∨ human(X))
。
2.
将否定向内移动
:处理应用于非原子公式的否定情况,进行适当的重写。例如,
~(human(caesar) & living(caesar))
会转换为
~human(caesar) ∨ ~living(caesar)
,
~all(Y, person(Y))
会转换为
exists(Y, ~person(Y))
。这一阶段的有效性基于以下恒等式:
~(α & β)
等价于
(~α) ∨ (~β)
,
~exists(v,P)
等价于
all(v, ~P)
,
~all(v,P)
等价于
exists(v, ~P)
。经过这一阶段,否定只会直接应用于原子公式,我们将原子命题或前面带有否定符号的原子命题称为文字。
3.
Skolem化
:去除存在量词,通过引入新的常量符号(Skolem常量)来代替由存在量词引入的变量。例如,
exists(X, female(X) & motherof(X, eve))
会通过Skolem化变为
female(gl97) & motherof(gl97, eve)
,其中
gl97
是一个未在其他地方使用的新常量。当公式中有全称量词时,Skolem化会更复杂,需要引入函数符号来表达存在的事物如何依赖于变量的选择。例如,
all(X, human(X) -> exists(Y, motherof(X, Y)))
应Skolem化为
all(X, human(X) -> motherof(X, g2(X)))
。
4.
将全称量词向外移动
:将任何全称量词移动到公式的外部,这不会影响公式的含义。例如,
all(X, man(X) -> all(Y, woman(Y) -> likes(X, Y)))
会转换为
all(X, all(Y, man(X) -> (woman(Y) -> likes(X, Y))))
。由于公式中的每个变量现在都由公式外部的全称量词引入,量词本身不再提供额外信息,我们可以通过省略量词来缩写公式,只需记住每个变量都由一个隐含的全称量词引入。
5.
分配
&
到
∨
:此时,原始的谓词演算公式已经发生了很大变化,没有了显式的量词,只剩下
&
和
∨
连接词(除了文字的否定)。我们将其转换为一种特殊的范式——合取范式,其中合取不再出现在析取内部。可以利用恒等式
(A & B) ∨ C
等价于
(A ∨ C) & (B ∨ C)
和
(A ∨ B) & C
等价于
(A & C) ∨ (B & C)
来进行转换。例如,
holiday(X) ∨ (work(chris, X) & (angry(chris) ∨ sad(chris)))
等价于
(holiday(X) ∨ work(chris, X)) & (holiday(X) ∨ (angry(chris) ∨ sad(chris)))
。
6.
转换为子句
:此时的公式通常由一组
&
连接的事物组成,这些事物要么是文字,要么是由
∨
连接的文字。我们可以忽略结构的嵌套和顺序,因为它们不影响公式的含义。最终,我们可以将公式表示为一组子句,每个子句由文字通过析取连接而成。例如,
(holiday(X) ∨ work(chris, X)) & (holiday(X) ∨ (angry(chris) ∨ sad(chris)))
会产生两个子句,第一个子句包含文字
holiday(X)
和
work(chris, X)
,第二个子句包含文字
holiday(X)
、
angry(chris)
和
sad(chris)
。
通过这六个阶段,我们将谓词演算公式转换为了子句形式,这种形式简洁明了,并且不改变公式是否存在使其为真的解释。子句形式由一组子句组成,每个子句是一组文字的集合,文字可以是原子公式或否定的原子公式。在理解子句形式的含义时,我们需要记住省略的隐含合取、析取和全称量词的约定。
综上所述,Prolog与逻辑有着密切的关系,通过谓词演算和子句形式的转换,我们可以更好地理解和运用Prolog进行逻辑编程。在实际应用中,我们可以根据具体需求,利用语法规则和逻辑转换来处理语言和解决各种问题。
4. 子句形式转换示例
为了更清晰地展示将谓词演算公式转换为子句形式的过程,我们来看一个具体的例子。假设我们有公式
all(X, all(Y, person(Y) -> respect(Y, X)) -> king(X))
,它表示如果每个人都尊重某个人,那么那个人就是国王。下面是该公式转换为子句形式的详细步骤:
4.1 去除蕴含
根据规则,将
->
进行替换。原公式
all(X, all(Y, person(Y) -> respect(Y, X)) -> king(X))
转换为:
all(X, ~all(Y, ~person(Y) ∨ respect(Y, X)) ∨ king(X))
4.2 将否定向内移动
利用否定的恒等式,将否定符号向内移动。
~all(Y, ~person(Y) ∨ respect(Y, X))
转换为
exists(Y, person(Y) & ~respect(Y, X))
,此时公式变为:
all(X, exists(Y, person(Y) & ~respect(Y, X)) ∨ king(X))
4.3 Skolem化
因为存在存在量词
exists(Y)
,且前面有全称量词
all(X)
,所以需要引入函数符号。这里引入函数
f(X)
来代替
Y
,Skolem化后的公式为:
all(X, person(f(X)) & ~respect(f(X), X) ∨ king(X))
4.4 将全称量词向外移动
由于全称量词已经在最外层,这一步无需改动,公式仍然是:
all(X, person(f(X)) & ~respect(f(X), X) ∨ king(X))
然后省略全称量词,得到:
person(f(X)) & ~respect(f(X), X) ∨ king(X)
4.5 分配
&
到
∨
根据恒等式
(A & B) ∨ C
等价于
(A ∨ C) & (B ∨ C)
,公式转换为:
(person(f(X)) ∨ king(X)) & (~respect(f(X), X) ∨ king(X))
4.6 转换为子句
最终得到两个子句:
- 子句 1:
{person(f(X)), king(X)}
- 子句 2:
{~respect(f(X), X), king(X)}
通过这个例子,我们可以更直观地看到谓词演算公式是如何一步步转换为子句形式的。
5. Prolog语法规则应用总结
5.1 语言逻辑转换总结
在将语言转换为逻辑形式时,我们通过定义语法规则来实现句子结构的分析和逻辑含义的提取。例如,对于英语句子的处理,我们可以使用DCGs(确定子句文法)规则将句子直接转换为谓词演算表示。以下是相关规则的总结:
?- op(500,xfy,&).
?- op(600,xfy,->).
sentence(P) -->
noun_phrase(X, PI, P), verb_phrase(X, PI).
noun_phrase(X, Pi, P) -->
determiner(X, P2, Pi, P),
noun(X, P3),
rel_clause(X, P3, P2).
noun_phrase(X, P, P) --> proper_noun(X).
verb_phrase(X, P) -->
trans_verb(X, Y, PI), noun_phrase(Y, PI, P).
verb_phrase(X, P) --> intrans_verb(X, P).
rel_clause(X, PI, (P1&P2)) -->
[that], verb_phrase(X, P2).
rel_clause(_, P, P) --> [].
determiner(X, PI, P2, all(X,(Pl -> P2))) --> [every].
determiner(X, PI, P2, exists(X, (P1&P2))) --> [a].
noun(X, man(X)) --> [man].
noun(X, woman(X)) --> [woman].
proper_noun(john) --> [john].
trans_verb(X, Y, loves(X,Y)) --> [loves].
intrans_verb(X, lives(X)) --> [lives].
这些规则通过参数构建表示短语含义的结构,帮助我们将自然语言句子转换为逻辑命题。
5.2 语法规则广泛应用总结
语法规则可以通过隐藏额外参数来跟踪计算过程中的信息变化。通过定义
'C'/3
谓词,我们可以灵活控制语法规则的行为。以下是不同应用场景下
'C'/3
的定义总结:
| 应用场景 |
'C'/3
定义 |
| ---- | ---- |
| 正常语法规则 |
'C'([W|Ws], W, Ws).
|
| 计算列表长度 |
'C'(Old, X, New) :- New is Old + X.
|
| 计算零件列表 |
'C'(Old, X, [X|Old]).
|
5.3 逻辑关系总结
Prolog与逻辑紧密相关,通过谓词演算和子句形式的转换,我们可以利用逻辑来处理语言和解决问题。谓词演算中的项、谓词符号、逻辑连接词和量词等概念为我们提供了表达和推理的工具,而子句形式则简化了逻辑公式的处理。
6. 总结与展望
6.1 总结
本文深入探讨了Prolog语法规则与逻辑关系的多个方面。我们学习了如何将语言转换为逻辑形式,通过定义语法规则实现句子结构的分析和逻辑含义的提取。同时,了解了语法规则的更广泛应用,通过隐藏额外参数和定义
'C'/3
谓词,我们可以灵活地跟踪计算过程中的信息变化。此外,详细介绍了谓词演算的基本概念,包括项、谓词符号、逻辑连接词和量词,并阐述了将谓词演算公式转换为子句形式的六个主要阶段。通过具体的例子,我们更清晰地看到了转换过程的实际应用。
6.2 展望
在未来的研究和应用中,我们可以进一步探索Prolog在自然语言处理、知识表示和推理等领域的潜力。例如,结合机器学习技术,提高Prolog在处理复杂语言和逻辑问题时的性能和准确性。同时,可以研究如何优化子句形式的转换算法,提高转换效率。此外,还可以拓展语法规则的应用范围,使其能够处理更多类型的信息和问题。
6.3 流程图总结
下面是将谓词演算公式转换为子句形式的整体流程图:
graph TD;
A[原始公式] --> B[去除蕴含];
B --> C[将否定向内移动];
C --> D[Skolem化];
D --> E[将全称量词向外移动];
E --> F[分配 `&` 到 `∨`];
F --> G[转换为子句];
通过这个流程图,我们可以更直观地看到整个转换过程的步骤和顺序。
总之,Prolog语法规则与逻辑关系的研究为我们提供了强大的工具和方法,帮助我们处理语言和解决逻辑问题。随着技术的不断发展,我们有望在更多领域看到Prolog的应用和创新。
超级会员免费看
610

被折叠的 条评论
为什么被折叠?



