本文根据笔者2017年的一次讲座整理。
语法描述了一个编程语言应该是什么样子,语义描述的是”是什么意思“。本文讲述CHR的标准操作语义(Standard Operational Semantics)。这套语义加入了防止重复应用规则的机制。
Numbered Constraints
给一个约束c c c 加上一个值为i i i 的ID,用c # i c\#i c # i 来表示,称为“numbered constraint”。定义如下两个函数用来操作c # i c\#i c # i 。
c h r ( c # i ) = c i d ( c # i ) = i chr(c\#i) = c \\
id(c\#i)=i
c h r ( c # i ) = c i d ( c # i ) = i
状态
执行过程每一步的状态,包括以下几个内容。
Goal
类型:多重集符号:G G G 内容:当前的Goal的内容。执行的过程中,Goal的内容是会变化的。
(Constraint) Store
类型:集合符号:S S S 内容:Numbered constraints。
传播历史(Propagation History)
类型:集合符号:T T T 内容:这是一个元组( r , I ) (r,I) ( r , I ) 的集合。
其中,
步骤ID
类型:正整数通常用符号:i i i 内容:随着执行步骤的增加,这个值可能会增加。这个值和T T T 的内容,用来防止重复应用规则。
为了表述方便,我们每一个状态都用< G , S , T > i <G,S,T>_i < G , S , T > i 来表示。其中,下标i i i 表示步骤ID。
程序执行前的初始状态为< G , ∅ , ∅ > 1 <G,\emptyset,\emptyset>_1 < G , ∅ , ∅ > 1 ,G G G 为一开始要执行的 Goal,也就是 Query。
规则
我们只考虑下面一种形式。
r @ H 1 \ H 2 ⇔ g ∣ C r @ H_{1} \backslash H_{2} \Leftrightarrow g | C
r @ H 1 \ H 2 ⇔ g ∣ C
这是一条 Simpagation Rule。其中,r r r 是规则名称,H 1 H_1 H 1 和H 2 H_2 H 2 是头部,g g g 是 Guard。C C C 是body部分。( Body 部分是 Goal,也就是一个多重集)。当 H 1 = ∅ H_1 = \emptyset H 1 = ∅ 的时候,这条规则是 Simplification Rule;当H 2 = ∅ H_2 = \emptyset H 2 = ∅ 的时候,这条规则是Propagation Rule。当 g g g 省略的时候,我们认为g = true g = \text{true} g = true ,相当于不再进行额外的检查。
关于匹配和( r , I ) (r,I) ( r , I )
所有的匹配,都是规则头部和store中的约束进行匹配。
匹配成功需要满足两个条件:
对规则头部的约束中的变量进行替换后,可以和Store中对应的约束内容保持一致。
Guard 中的条件被满足。
匹配后,变量被初始化成替换的值。
下面举一个例子说明。
假设,规则r 1 r1 r 1 头部H 1 = { p ( X ) , q ( X ) , p ( X ) } , H 2 = { r ( Y ) } H_1 = \{ p(X),q(X),p(X) \},H_2 = \{ r(Y) \} H 1 = { p ( X ) , q ( X ) , p ( X ) } , H 2 = { r ( Y ) } ,g g g 省略,Store 中的内容 S = { p ( 1 ) # 1 , q ( 1 ) # 2 , q ( 1 ) # 3 , r ( 10 ) # 4 , p ( 1 ) # 5 } S = \{p(1)\#1,q(1)\#2,q(1)\#3,r(10)\#4,p(1)\#5\} S = { p ( 1 ) # 1 , q ( 1 ) # 2 , q ( 1 ) # 3 , r ( 1 0 ) # 4 , p ( 1 ) # 5 } 。若取X = 1 , Y = 10 X=1,Y=10 X = 1 , Y = 1 0 ,则规则头部可以成功匹配到Store中的内容。此时规则名称、规则序列组成的( r , I ) (r,I) ( r , I ) 可能为{ r 1 , ( 1 , 2 , 5 , 4 ) } \{ r1, (1,2,5,4)\} { r 1 , ( 1 , 2 , 5 , 4 ) } 或{ r 1 , ( 1 , 3 , 5 , 4 ) } \{r1, (1,3,5,4)\} { r 1 , ( 1 , 3 , 5 , 4 ) } 或{ r 1 , ( 5 , 3 , 1 , 4 ) } \{r1,(5,3,1,4)\} { r 1 , ( 5 , 3 , 1 , 4 ) } 或{ r 1 , ( 5 , 2 , 1 , 4 ) } \{r1,(5,2,1,4)\} { r 1 , ( 5 , 2 , 1 , 4 ) } 。每一次匹配只对应其中一种可能。匹配后,变量X X X 和Y Y Y 被初始化成1和10.
执行过程
执行过程分成3个基本操作。通过不断应用这3个基本操作,来变换当前的状态,直到无法再应用为止。这三个操作分别是 Solve 、Introduce 和Apply 。
Solve
使用前提:G G G 中存在一个可以被执行的builtin constraint。执行过程:执行该builtin constraint,并从G G G 中移除。
Introduce
当前状态:< G , S , T > i <G,S,T>_i < G , S , T > i 。使用前提:G G G 中存在一个CHR Constraint c c c 。执行过程:
从G G G 中移除c c c 。
往S S S 中添加c # i c\#i c # i 。
i ← i + 1 i \leftarrow i+1 i ← i + 1 。
Apply
当前状态:< G , S , T > i <G,S,T>_i < G , S , T > i 。使用前提:假设规则为r @ H 1 \ H 2 ⇔ g ∣ C r @ H_{1} \backslash H_{2} \Leftrightarrow g | C r @ H 1 \ H 2 ⇔ g ∣ C 。规则r r r 可以匹配上S S S 中的约束,对应本次匹配的( r , I ) ∉ T (r,I) \notin T ( r , I ) ∈ / T 执行过程:
为了避免和已有的变量名重复,替换规则r r r 中所有的变量名成为独一无二的变量名。
规则r r r 和S S S 中的约束进行匹配,并初始化相应的变量。
将对应本次匹配的( r , I ) (r,I) ( r , I ) 添加进T T T 。
从S S S 中删除和H 2 H_2 H 2 匹配上的元素。
把C C C 的内容添加到G G G 中。
对于执行过程的第1步,要注意的是,变量名本身并不重要,变量名并不属于变量的一部分,只要能区分开即可。而同一个规则多次应用,由于每次都替换变量名,所以每次应用时的变量也都不同,不会造成混淆。例如,有规则p(X) <=> X1 is X - 1, p(X1)
和查询p(9)
,第一次应用规则后变成X1 is 9 - 1, p(X1)
,接着再应用一次规则,若没有修改变量的名字,就会变成X1 is 9 - 1, X1 is X1 - 1, p(X1)
,然而这其中不同的X1
代表着不同的值,直接就混淆了。变量重命名是很重要的一个步骤。
以上每一个基本操作的使用并没有顺序的要求,因而可以以任意次序应用这些操作,而程序执行的结果也可能因此而不同 。
执行示例1
已知CHR规则如下。
1 2 gcd1 @ gcd(0 ) <=> true. gcd2 @ gcd(I ) \ gcd(J ) <=> I =< J | K is J - I , gcd(K ).
这是一段求最大公约数的程序。执行 gcd(6), gcd(9).
,结果如下。
1 2 ?- gcd(6 ), gcd(9 ). gcd(3 ).
下面从初始状态< { gcd ( 6 ) , gcd ( 9 ) } , ∅ , ∅ > 1 <\{\operatorname{gcd}(6), \operatorname{gcd}(9)\}, \emptyset, \emptyset>_{1} < { g c d ( 6 ) , g c d ( 9 ) } , ∅ , ∅ > 1 开始,展示在标准操作语义下的执行过程。
< { gcd ( 6 ) , g c d ( 9 ) } , ∅ , ∅ > 1 ⟼ i n t r o d u c e < { g c d ( 9 ) } , { g c d ( 6 ) # 1 } , ∅ > 2 ⟼ i n t r o d u c e < ∅ , { g c d ( 6 ) # 1 , gcd ( 9 ) # 2 } , ∅ > 3 ⟼ a p p l y gcd2 < { K 1 is 9 − 6 , g c d ( K 1 ) } , { g c d ( 6 ) # 1 } , { ( g c d 2 , ( 1 , 2 ) ) } > 3 ⟼ s o l v e < { g c d ( 3 ) } , { g c d ( 6 ) # 1 } , { ( g c d 2 , ( 1 , 2 ) ) } > 3 ⟼ i n t r o d u c e < ∅ , { g c d ( 6 ) # 1 , gcd ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) } > 4 ⟼ a p p l y gcd2 < { K 2 is 6 − 3 , gcd ( K 2 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) } > 4 ⟼ s o l v e < { g c d ( 3 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) } > 4 ⟼ i n t r o d u c e < ∅ , { g c d ( 3 ) # 3 , gcd ( 3 ) # 4 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) } > 5 ⟼ a p p l y gcd2 < { K 3 is 3 − 3 , g c d ( K 3 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) } > 5 ⟼ s o l v e < { g c d ( 0 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) } > 5 ⟼ i n t r o d u c e < ∅ , { g c d ( 3 ) # 3 , g c d ( 0 ) # 5 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) } } > 6 ⟼ a p p l y gcd1 < { t r u e } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) , ( g c d 1 , ( 5 ) ) } > 6 ⟼ s o l v e < ∅ , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) , ( g c d 1 , ( 5 ) ) } > 6 \begin{array}{ll}
& <\{\gcd(6), \mathrm{gcd}(9)\}, \emptyset, \emptyset>_1 \\
\longmapsto_{introduce} & <\{\mathrm{gcd}(9)\},\{\mathrm{gcd}(6) \# 1\}, \emptyset>_{2} \\
\longmapsto_{i n t r o d u c e} & <\emptyset,\{\mathrm{gcd}(6) \# 1, \operatorname{gcd}(9) \# 2\}, \emptyset>_3 \\
\longmapsto_{apply \text{ gcd2}} & <\left\{K_{1 }\text{ is } 9-6, \mathrm{gcd}\left(K_{1}\right)\right\},\{\mathrm{gcd}(6) \# 1\},\{(\mathrm{gcd} 2,(1,2))\}>_{3} \\
\longmapsto_{solve} & <\{\mathrm{gcd}(3)\},\{\mathrm{gcd}(6) \# 1\},\{(\mathrm{gcd} 2,(1,2))\}>_{3} \\
\longmapsto_{introduce} & <\emptyset,\{\mathrm{gcd}(6) \# 1, \operatorname{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2))\}>_{4} \\
\longmapsto_{apply \text{ gcd2}} & <\left\{K_{2} \text{ is } 6-3, \operatorname{gcd}\left(K_{2}\right)\right\},\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1))\}>_{4} \\
\longmapsto_{solve} & <\{\mathrm{gcd}(3)\},\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)\}>_4 \\
\longmapsto_{introduce} & <\emptyset,\{\mathrm{gcd}(3) \# 3, \operatorname{gcd}(3) \# 4\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1))\}>_5 \\
\longmapsto_{apply \text{ gcd2}} & <\left\{K_{3} \text{ is } 3-3, \mathrm{gcd}\left(K_{3}\right)\right\},\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)),(\mathrm{gcd} 2,(3,4))\}>_{5} \\
\longmapsto_{solve} & <\{\mathrm{gcd}(0)\},\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)),(\mathrm{gcd} 2,(3,4))\}>_{5} \\
\longmapsto_{introduce} & <\emptyset,\{\mathrm{gcd}(3) \# 3, \mathrm{gcd}(0) \# 5\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)),(\mathrm{gcd} 2,(3,4)\}\}>_{6} \\
\longmapsto_{apply \text{ gcd1}} & <\{\mathrm{true}\},\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)),(\mathrm{gcd} 2,(3,4)),(\mathrm{gcd} 1,(5))\}>_{6} \\
\longmapsto_{solve} & <\emptyset,\{\mathrm{gcd}(3) \# 3\},\{(\mathrm{gcd} 2,(1,2)),(\mathrm{gcd} 2,(3,1)),(\mathrm{gcd} 2,(3,4)),(\mathrm{gcd} 1,(5))\}>_{6}
\end{array}
⟼ i n t r o d u c e ⟼ i n t r o d u c e ⟼ a p p l y gcd2 ⟼ s o l v e ⟼ i n t r o d u c e ⟼ a p p l y gcd2 ⟼ s o l v e ⟼ i n t r o d u c e ⟼ a p p l y gcd2 ⟼ s o l v e ⟼ i n t r o d u c e ⟼ a p p l y gcd1 ⟼ s o l v e < { g cd( 6 ) , g c d ( 9 ) } , ∅ , ∅ > 1 < { g c d ( 9 ) } , { g c d ( 6 ) # 1 } , ∅ > 2 < ∅ , { g c d ( 6 ) # 1 , g c d ( 9 ) # 2 } , ∅ > 3 < { K 1 is 9 − 6 , g c d ( K 1 ) } , { g c d ( 6 ) # 1 } , { ( g c d 2 , ( 1 , 2 ) ) } > 3 < { g c d ( 3 ) } , { g c d ( 6 ) # 1 } , { ( g c d 2 , ( 1 , 2 ) ) } > 3 < ∅ , { g c d ( 6 ) # 1 , g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) } > 4 < { K 2 is 6 − 3 , g c d ( K 2 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) } > 4 < { g c d ( 3 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) } > 4 < ∅ , { g c d ( 3 ) # 3 , g c d ( 3 ) # 4 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) } > 5 < { K 3 is 3 − 3 , g c d ( K 3 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) } > 5 < { g c d ( 0 ) } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) } > 5 < ∅ , { g c d ( 3 ) # 3 , g c d ( 0 ) # 5 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) } } > 6 < { t r u e } , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) , ( g c d 1 , ( 5 ) ) } > 6 < ∅ , { g c d ( 3 ) # 3 } , { ( g c d 2 , ( 1 , 2 ) ) , ( g c d 2 , ( 3 , 1 ) ) , ( g c d 2 , ( 3 , 4 ) ) , ( g c d 1 , ( 5 ) ) } > 6
最终状态的Store S = { g c d ( 3 ) # 3 } S = \{\mathrm{gcd}(3) \# 3\} S = { g c d ( 3 ) # 3 } 即为最终结果。
实际上对于这个程序,若在最后不停重复应用 gcd2
,那么程序是不会终止的。这种情况下,传播历史T T T 并无法限制住这种重复应用(在两次应用中间 ,执行introduce操作,可导致这种重复应用不会停止)。
完整程序可以在这里 下载。
执行示例2
已知CHR规则如下。
1 2 3 r1 @ mother(X , Y ) ==> parent(X , Y ). r2 @ father(X , Y ) ==> parent(X , Y ). r3 @ parent(X , Z ), parent(Y , Z ) ==> sibling(X , Y ).
对以上规则执行查询mother(hans, mira), mother(sepp, mira), father(sepp, john).
,结果如下。
1 2 3 4 5 6 7 8 9 ?- mother(hans, mira), mother(sepp, mira), father(sepp, john). mother(sepp, mira), mother(hans, mira), father(sepp, john), parent(sepp, john), parent(sepp, mira), parent(hans, mira), sibling(hans, sepp), sibling(sepp, hans).
下面展示在标准操作语义下的执行过程。
操作
G G G
S S S
T T T
i i i
初始状态
mother(hans, mira)
mother(sepp, mira)
father(sepp, john)
∅ \emptyset ∅
∅ \emptyset ∅
1
introduce
mother(sepp, mira)
father(sepp, john)
mother(hans, mira)#l
∅ \emptyset ∅
2
apply r 1 r1 r 1
parent(hans, mira)
mother(sepp, mira)
father(sepp, john)
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) )
2
introduce
mother(sepp, mira)
father(sepp, john)
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) )
3
introduce
father(sepp, john)
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) )
4
apply r 1 r1 r 1
parent(sepp, mira)
father(sepp, john)
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) )
4
introduce
father(sepp, john)
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) )
5
apply r 3 r3 r 3
sibling(sepp, hans)
father(sepp, john)
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) )
5
apply r 3 r3 r 3
sibling(hans, sepp)
sibling(sepp, hans)
father(sepp, john)
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) )
5
introduce
sibling(sepp, hans)
father(sepp, john)
sibling(hans, sepp)#5
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) )
6
introduce
father(sepp, john)
sibling(sepp, hans)#6
sibling(hans, sepp)#5
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) )
7
introduce
∅ \emptyset ∅
father(sepp, john)#7
sibling(sepp, hans)#6
sibling(hans, sepp)#5
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) )
8
apply r 2 r2 r 2
parent(sepp, john)
father(sepp, john)#7
sibling(sepp, hans)#6
sibling(hans, sepp)#5
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) ) ( r 2 , ( 7 ) ) (r2, (7)) ( r 2 , ( 7 ) )
8
introduce
∅ \emptyset ∅
parent(sepp, john)#8
father(sepp, john)#7
sibling(sepp, hans)#6
sibling(hans, sepp)#5
parent (sepp, mira)#4
mother(sepp, mira)#3
parent(hans, mira)#2
mother(hans, mira)#l
( r 1 , ( 1 ) ) (r1, (1)) ( r 1 , ( 1 ) ) ( r 1 , ( 3 ) ) (r1, (3)) ( r 1 , ( 3 ) ) ( r 3 , ( 4 , 2 ) ) (r3, (4,2)) ( r 3 , ( 4 , 2 ) ) ( r 3 , ( 2 , 4 ) ) (r3, (2,4)) ( r 3 , ( 2 , 4 ) ) ( r 2 , ( 7 ) ) (r2, (7)) ( r 2 , ( 7 ) )
9
S S S 中的内容即为执行结果。
完整程序可以在这里 下载。