@@ -79,7 +79,7 @@ def tell(self, sentence):
79
79
self .clauses .extend (conjuncts (to_cnf (sentence )))
80
80
81
81
def ask_generator (self , query ):
82
- "Yield the empty substitution if KB implies query."
82
+ "Yield the empty substitution if KB implies query; else nothing ."
83
83
if tt_entails (Expr ('&' , * self .clauses ), query ):
84
84
yield {}
85
85
@@ -316,18 +316,23 @@ def parse_definite_clause(s):
316
316
#______________________________________________________________________________
317
317
318
318
def tt_entails (kb , alpha ):
319
- """Does kb entail the sentence alpha? Use truth tables. [Fig. 7.10]
319
+ """Does kb entail the sentence alpha? Use truth tables. For propositional
320
+ kb's and sentences. [Fig. 7.10]
320
321
>>> tt_entails(expr('P & Q'), expr('Q'))
321
322
True
322
323
"""
324
+ assert not variables (alpha )
323
325
return tt_check_all (kb , alpha , prop_symbols (kb & alpha ), {})
324
326
325
327
def tt_check_all (kb , alpha , symbols , model ):
326
328
"Auxiliary routine to implement tt_entails."
327
329
if not symbols :
328
- if pl_true (kb , model ): return pl_true (alpha , model )
329
- else : return True
330
- assert result is not None
330
+ if pl_true (kb , model ):
331
+ result = pl_true (alpha , model )
332
+ assert result in (True , False )
333
+ return result
334
+ else :
335
+ return True
331
336
else :
332
337
P , rest = symbols [0 ], symbols [1 :]
333
338
return (tt_check_all (kb , alpha , rest , extend (model , P , True )) and
@@ -344,7 +349,8 @@ def prop_symbols(x):
344
349
for symbol in prop_symbols (arg )))
345
350
346
351
def tt_true (alpha ):
347
- """Is the sentence alpha a tautology? (alpha will be coerced to an expr.)
352
+ """Is the propositional sentence alpha a tautology? (alpha will be
353
+ coerced to an expr.)
348
354
>>> tt_true(expr("(P >> Q) <=> (~P | Q)"))
349
355
True
350
356
"""
@@ -472,20 +478,19 @@ def distribute_and_over_or(s):
472
478
"""
473
479
if s .op == '|' :
474
480
s = NaryExpr ('|' , * s .args )
481
+ if s .op != '|' :
482
+ return distribute_and_over_or (s )
475
483
if len (s .args ) == 0 :
476
484
return FALSE
477
485
if len (s .args ) == 1 :
478
486
return distribute_and_over_or (s .args [0 ])
479
487
conj = find_if ((lambda d : d .op == '&' ), s .args )
480
488
if not conj :
481
- return NaryExpr ( s . op , * s . args )
489
+ return s
482
490
others = [a for a in s .args if a is not conj ]
483
- if len (others ) == 1 :
484
- rest = others [0 ]
485
- else :
486
- rest = NaryExpr ('|' , * others )
487
- return NaryExpr ('&' , * map (distribute_and_over_or ,
488
- [(c | rest ) for c in conj .args ]))
491
+ rest = NaryExpr ('|' , * others )
492
+ return NaryExpr ('&' , * [distribute_and_over_or (c | rest )
493
+ for c in conj .args ])
489
494
elif s .op == '&' :
490
495
return NaryExpr ('&' , * map (distribute_and_over_or , s .args ))
491
496
else :
@@ -584,7 +589,7 @@ def tell(self, sentence):
584
589
self .clauses .append (sentence )
585
590
586
591
def ask_generator (self , query ):
587
- "Yield the empty substitution if KB implies query."
592
+ "Yield the empty substitution if KB implies query; else nothing ."
588
593
if pl_fc_entails (self .clauses , query ):
589
594
yield {}
590
595
@@ -794,10 +799,10 @@ def unify(x, y, s):
794
799
return unify_var (y , x , s )
795
800
elif isinstance (x , Expr ) and isinstance (y , Expr ):
796
801
return unify (x .args , y .args , unify (x .op , y .op , s ))
797
- elif isinstance (x , str ) or isinstance (y , str ) or not x or not y :
798
- # orig. return if_(x == y, s, None) but we already know x != y
802
+ elif isinstance (x , str ) or isinstance (y , str ):
799
803
return None
800
- elif issequence (x ) and issequence (y ) and len (x ) == len (y ) and x :
804
+ elif issequence (x ) and issequence (y ) and len (x ) == len (y ):
805
+ if not x : return s
801
806
return unify (x [1 :], y [1 :], unify (x [0 ], y [0 ], s ))
802
807
else :
803
808
return None
@@ -833,7 +838,6 @@ def occur_check(var, x, s):
833
838
def extend (s , var , val ):
834
839
"""Copy the substitution s and extend it by setting var to val;
835
840
return copy.
836
-
837
841
>>> ppsubst(extend({x: 1}, y, 2))
838
842
{x: 1, y: 2}
839
843
"""
@@ -859,7 +863,7 @@ def subst(s, x):
859
863
860
864
def fol_fc_ask (KB , alpha ):
861
865
"""Inefficient forward chaining for first-order logic. [Fig. 9.3]
862
- KB is an FOLHornKB and alpha must be an atomic sentence."""
866
+ KB is a FolKB and alpha must be an atomic sentence."""
863
867
while True :
864
868
new = {}
865
869
for r in KB .clauses :
@@ -883,20 +887,19 @@ def standardize_apart(sentence, dic=None):
883
887
if sentence in dic :
884
888
return dic [sentence ]
885
889
else :
886
- standardize_apart .counter += 1
887
- v = Expr ('v_%d' % standardize_apart .counter )
890
+ v = Expr ('v_%d' % standardize_apart .counter .next ())
888
891
dic [sentence ] = v
889
892
return v
890
893
else :
891
894
return Expr (sentence .op ,
892
895
* [standardize_apart (a , dic ) for a in sentence .args ])
893
896
894
- standardize_apart .counter = 0
897
+ standardize_apart .counter = itertools . count ()
895
898
896
899
#______________________________________________________________________________
897
900
898
- class FolKB (KB ):
899
- """A knowledge base consisting of first-order definite clauses
901
+ class FolKB (KB ):
902
+ """A knowledge base consisting of first-order definite clauses.
900
903
>>> kb0 = FolKB([expr('Farmer(Mac)'), expr('Rabbit(Pete)'),
901
904
... expr('(Rabbit(r) & Farmer(f)) ==> Hates(f, r)')])
902
905
>>> kb0.tell(expr('Rabbit(Flopsie)'))
@@ -906,7 +909,6 @@ class FolKB (KB):
906
909
>>> kb0.ask(expr('Wife(Pete, x)'))
907
910
False
908
911
"""
909
-
910
912
def __init__ (self , initial_clauses = []):
911
913
self .clauses = [] # inefficient: no indexing
912
914
for clause in initial_clauses :
@@ -924,15 +926,13 @@ def ask_generator(self, query):
924
926
def retract (self , sentence ):
925
927
self .clauses .remove (sentence )
926
928
927
- def test_ask (q , kb = None ):
928
- e = expr (q )
929
- vars = variables (e )
930
- ans = fol_bc_ask (kb or test_kb , [e ])
931
- res = []
932
- for a in ans :
933
- res .append (pretty (dict ([(x , v ) for (x , v ) in a .items () if x in vars ])))
934
- res .sort (key = str )
935
- return res
929
+ def test_ask (query , kb = None ):
930
+ q = expr (query )
931
+ vars = variables (q )
932
+ answers = fol_bc_ask (kb or test_kb , [q ])
933
+ return sorted ([pretty (dict ((x , v ) for x , v in a .items () if x in vars ))
934
+ for a in answers ],
935
+ key = repr )
936
936
937
937
test_kb = FolKB (
938
938
map (expr , ['Farmer(Mac)' ,
@@ -991,7 +991,7 @@ def fol_bc_ask(KB, goals, theta={}):
991
991
for ans in fol_bc_ask (KB , new_goals , subst_compose (theta1 , theta )):
992
992
yield ans
993
993
994
- def subst_compose (s1 , s2 ):
994
+ def subst_compose (s1 , s2 ):
995
995
"""Return the substitution which is equivalent to applying s2 to
996
996
the result of applying s1 to an expression.
997
997
@@ -1021,18 +1021,9 @@ def subst_compose (s1, s2):
1021
1021
>>> subst(subst_compose(s2, s1), p) == subst(s1, subst(s2, p))
1022
1022
True
1023
1023
"""
1024
- sc = {}
1025
- for x , v in s1 .items ():
1026
- if s2 .has_key (v ):
1027
- w = s2 [v ]
1028
- sc [x ] = w # x -> v -> w
1029
- else :
1030
- sc [x ] = v
1031
- for x , v in s2 .items ():
1032
- if not (s1 .has_key (x )):
1033
- sc [x ] = v
1034
- # otherwise s1[x] preemptys s2[x]
1035
- return sc
1024
+ result = dict ((x , s2 .get (v , v )) for x , v in s1 .items ())
1025
+ result .update ((x , v ) for x , v in s2 .items () if x not in s1 )
1026
+ return result
1036
1027
1037
1028
#______________________________________________________________________________
1038
1029
0 commit comments