@@ -345,6 +345,16 @@ def is_definite_clause(s):
345
345
or (antecedent .op == '&'
346
346
and all (is_symbol (arg .op ) for arg in antecedent .args ))))
347
347
348
+ def parse_definite_clause (s ):
349
+ "Return the antecedents and the consequent of a definite clause."
350
+ assert is_definite_clause (s )
351
+ if is_symbol (s .op ):
352
+ return [], s
353
+ antecedent , consequent = s .args
354
+ antecedent = NaryExpr ('&' , antecedent )
355
+ antecedents = antecedent .args if antecedent .op == '&' else [antecedent ]
356
+ return antecedents , consequent
357
+
348
358
## Useful constant Exprs used in examples and code:
349
359
TRUE , FALSE , ZERO , ONE , TWO = map (Expr , ['TRUE' , 'FALSE' , 0 , 1 , 2 ])
350
360
A , B , C , F , G , P , Q , x , y , z = map (Expr , 'ABCFGPQxyz' )
@@ -914,8 +924,7 @@ def fol_fc_ask(KB, alpha):
914
924
while True :
915
925
new = {}
916
926
for r in KB .clauses :
917
- r1 = standardize_apart (r )
918
- ps , q = conjuncts (r .args [0 ]), r .args [1 ]
927
+ ps , q = parse_definite_clause (standardize_apart (r ))
919
928
raise NotImplementedError
920
929
921
930
def standardize_apart (sentence , dic = None ):
@@ -947,7 +956,6 @@ def standardize_apart(sentence, dic=None):
947
956
948
957
#______________________________________________________________________________
949
958
950
-
951
959
class FolKB (KB ):
952
960
"""A knowledge base consisting of first-order definite clauses
953
961
>>> kb0 = FolKB([expr('Farmer(Mac)'), expr('Rabbit(Pete)'),
@@ -977,10 +985,10 @@ def ask_generator(self, query):
977
985
def retract (self , sentence ):
978
986
self .clauses .remove (sentence )
979
987
980
- def test_ask (q ):
988
+ def test_ask (q , kb = None ):
981
989
e = expr (q )
982
990
vars = variables (e )
983
- ans = fol_bc_ask (test_kb , [e ])
991
+ ans = fol_bc_ask (kb or test_kb , [e ])
984
992
res = []
985
993
for a in ans :
986
994
res .append (pretty (dict ([(x , v ) for (x , v ) in a .items () if x in vars ])))
@@ -1003,11 +1011,22 @@ def test_ask(q):
1003
1011
])
1004
1012
)
1005
1013
1006
-
1014
+ crime_kb = FolKB (
1015
+ map (expr ,
1016
+ ['(American(x) & Weapon(y) & Sells(x, y, z) & Hostile(z)) ==> Criminal(x)' ,
1017
+ 'Owns(Nono, M1)' ,
1018
+ 'Missile(M1)' ,
1019
+ '(Missile(x) & Owns(Nono, x)) ==> Sells(West, x, Nono)' ,
1020
+ 'Missile(x) ==> Weapon(x)' ,
1021
+ 'Enemy(x, America) ==> Hostile(x)' ,
1022
+ 'American(West)' ,
1023
+ 'Enemy(Nono, America)'
1024
+ ])
1025
+ )
1026
+
1007
1027
def fol_bc_ask (KB , goals , theta = {}):
1008
1028
"""A simple backward-chaining algorithm for first-order logic. [Fig. 9.6]
1009
1029
KB should be an instance of FolKB, and goals a list of literals.
1010
-
1011
1030
>>> test_ask('Farmer(x)')
1012
1031
['{x: Mac}']
1013
1032
>>> test_ask('Human(x)')
@@ -1018,40 +1037,21 @@ def fol_bc_ask(KB, goals, theta={}):
1018
1037
['{x: MrsMac, y: Mac}', '{x: MrsRabbit, y: Pete}']
1019
1038
>>> test_ask('Rabbit(x)')
1020
1039
['{x: MrsRabbit}', '{x: Pete}']
1040
+ >>> test_ask('Criminal(x)', crime_kb)
1041
+ ['{x: West}']
1021
1042
"""
1022
-
1023
- if goals == []:
1043
+ if not goals :
1024
1044
yield theta
1025
- raise StopIteration ()
1026
-
1045
+ return
1027
1046
q1 = subst (theta , goals [0 ])
1028
-
1029
1047
for r in KB .clauses :
1030
- sar = standardize_apart (r )
1031
-
1032
- # Split into head and body
1033
- if is_symbol (sar .op ):
1034
- head = sar
1035
- body = []
1036
- elif sar .op == '>>' : # sar = (Body1 & Body2 & ...) >> Head
1037
- head = sar .args [1 ]
1038
- body = sar .args [0 ] # as conjunction
1039
- else :
1040
- raise Exception ("Invalid clause in FolKB: %s" % r )
1041
-
1042
- theta1 = unify (head , q1 , {})
1043
-
1048
+ ps , q = parse_definite_clause (standardize_apart (r ))
1049
+ theta1 = unify (q , q1 , {})
1044
1050
if theta1 is not None :
1045
- if body == []:
1046
- new_goals = goals [1 :]
1047
- else :
1048
- new_goals = conjuncts (body ) + goals [1 :]
1049
-
1051
+ new_goals = ps + goals [1 :]
1050
1052
for ans in fol_bc_ask (KB , new_goals , subst_compose (theta1 , theta )):
1051
1053
yield ans
1052
1054
1053
- raise StopIteration ()
1054
-
1055
1055
def subst_compose (s1 , s2 ):
1056
1056
"""Return the substitution which is equivalent to applying s2 to
1057
1057
the result of applying s1 to an expression.
0 commit comments