26
26
27
27
from __future__ import generators
28
28
import re
29
+ import itertools
29
30
import agents
30
31
from utils import *
31
32
32
33
#______________________________________________________________________________
33
34
34
35
class KB :
35
- """A Knowledge base to which you can tell and ask sentences.
36
+ """A knowledge base to which you can tell and ask sentences.
36
37
To create a KB, first subclass this class and implement
37
38
tell, ask_generator, and retract. Why ask_generator instead of ask?
38
39
The book is a bit vague on what ask means --
@@ -50,42 +51,40 @@ def tell(self, sentence):
50
51
abstract
51
52
52
53
def ask (self , query ):
53
- """Ask returns a substitution that makes the query true, or
54
- it returns False. It is implemented in terms of ask_generator."""
55
- try :
56
- return self .ask_generator (query ).next ()
57
- except StopIteration :
58
- return False
54
+ """Return a substitution that makes the query true, or,
55
+ failing that, return False."""
56
+ for result in self .ask_generator (query ):
57
+ return result
58
+ return False
59
59
60
60
def ask_generator (self , query ):
61
61
"Yield all the substitutions that make query true."
62
62
abstract
63
63
64
64
def retract (self , sentence ):
65
- "Remove the sentence from the KB"
65
+ "Remove sentence from the KB. "
66
66
abstract
67
67
68
68
69
69
class PropKB (KB ):
70
- "A KB for Propositional Logic. Inefficient, with no indexing."
70
+ "A KB for propositional logic. Inefficient, with no indexing."
71
71
72
72
def __init__ (self , sentence = None ):
73
73
self .clauses = []
74
74
if sentence :
75
75
self .tell (sentence )
76
76
77
77
def tell (self , sentence ):
78
- "Add the sentence's clauses to the KB"
78
+ "Add the sentence's clauses to the KB. "
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; else False"
83
- if not tt_entails (Expr ('&' , * self .clauses ), query ):
84
- return
85
- yield {}
82
+ "Yield the empty substitution if KB implies query."
83
+ if tt_entails (Expr ('&' , * self .clauses ), query ):
84
+ yield {}
86
85
87
86
def retract (self , sentence ):
88
- "Remove the sentence's clauses from the KB"
87
+ "Remove the sentence's clauses from the KB. "
89
88
for c in conjuncts (to_cnf (sentence )):
90
89
if c in self .clauses :
91
90
self .clauses .remove (c )
@@ -95,23 +94,23 @@ def retract(self, sentence):
95
94
class KB_Agent (agents .Agent ):
96
95
"""A generic logical knowledge-based agent. [Fig. 7.1]"""
97
96
def __init__ (self , KB ):
98
- t = 0
97
+ steps = itertools . count ()
99
98
def program (percept ):
99
+ t = steps .next ()
100
100
KB .tell (self .make_percept_sentence (percept , t ))
101
101
action = KB .ask (self .make_action_query (t ))
102
102
KB .tell (self .make_action_sentence (action , t ))
103
- t = t + 1
104
103
return action
105
104
self .program = program
106
105
107
- def make_percept_sentence (self , percept , t ):
108
- return ( Expr ("Percept" )(percept , t ) )
106
+ def make_percept_sentence (self , percept , t ):
107
+ return Expr ("Percept" )(percept , t )
109
108
110
- def make_action_query (self , t ):
111
- return ( expr ("ShouldDo(action, %d)" % t ) )
109
+ def make_action_query (self , t ):
110
+ return expr ("ShouldDo(action, %d)" % t )
112
111
113
112
def make_action_sentence (self , action , t ):
114
- return ( Expr ("Did" )(action , t ) )
113
+ return Expr ("Did" )(action , t )
115
114
116
115
#______________________________________________________________________________
117
116
@@ -175,13 +174,13 @@ def __call__(self, *args):
175
174
176
175
def __repr__ (self ):
177
176
"Show something like 'P' or 'P(x, y)', or '~P' or '(P | Q | R)'"
178
- if len ( self .args ) == 0 : # Constant or proposition with arity 0
177
+ if not self .args : # Constant or proposition with arity 0
179
178
return str (self .op )
180
- elif is_symbol (self .op ): # Functional or Propositional operator
179
+ elif is_symbol (self .op ): # Functional or propositional operator
181
180
return '%s(%s)' % (self .op , ', ' .join (map (repr , self .args )))
182
181
elif len (self .args ) == 1 : # Prefix operator
183
182
return self .op + repr (self .args [0 ])
184
- else : # Infix operator
183
+ else : # Infix operator
185
184
return '(%s)' % (' ' + self .op + ' ' ).join (map (repr , self .args ))
186
185
187
186
def __eq__ (self , other ):
@@ -215,7 +214,7 @@ def __neg__(self): return Expr('-', self)
215
214
def __or__ (self , other ): return Expr ('|' , self , other )
216
215
def __pow__ (self , other ): return Expr ('**' , self , other )
217
216
def __xor__ (self , other ): return Expr ('^' , self , other )
218
- def __mod__ (self , other ): return Expr ('<=>' , self , other ) ## (x % y)
217
+ def __mod__ (self , other ): return Expr ('<=>' , self , other )
219
218
220
219
221
220
@@ -246,7 +245,7 @@ def expr(s):
246
245
247
246
def is_symbol (s ):
248
247
"A string s is a symbol if it starts with an alphabetic char."
249
- return isinstance (s , str ) and s [0 ].isalpha ()
248
+ return isinstance (s , str ) and s [: 1 ].isalpha ()
250
249
251
250
def is_var_symbol (s ):
252
251
"A logic variable symbol is an initial-lowercase string."
@@ -276,38 +275,38 @@ def is_negative(s):
276
275
return s .op == '~'
277
276
278
277
def is_literal (s ):
279
- """s is a FOL literal
278
+ """Is s a FOL literal?
280
279
>>> is_literal(expr('~F(A, B)'))
281
280
True
282
281
>>> is_literal(expr('F(A, B)'))
283
282
True
284
283
>>> is_literal(expr('F(A, B) & G(B, C)'))
285
284
False
285
+ >>> is_literal(expr('~~A'))
286
+ False
287
+ >>> is_literal(expr('x')) # XXX I guess this is intended?
288
+ True
286
289
"""
287
- return is_symbol (s .op ) or (s .op == '~' and is_literal (s .args [0 ]))
290
+ return is_symbol (s .op ) or (s .op == '~' and is_symbol (s .args [0 ]. op ))
288
291
289
292
def literals (s ):
290
- """returns the list of literals of logical expression s.
293
+ """Return a list of the literals in expression s.
291
294
>>> literals(expr('F(A, B)'))
292
295
[F(A, B)]
293
296
>>> literals(expr('~F(A, B)'))
294
297
[~F(A, B)]
295
298
>>> literals(expr('(F(A, B) & G(B, C)) ==> R(A, C)'))
296
299
[F(A, B), G(B, C), R(A, C)]
297
300
"""
298
- op = s .op
299
- if op in set (['&' , '|' , '<<' , '>>' , '%' , '^' ]):
300
- result = []
301
- for arg in s .args :
302
- result .extend (literals (arg ))
303
- return result
304
- elif is_literal (s ):
301
+ if is_literal (s ):
305
302
return [s ]
306
303
else :
307
- return []
304
+ return flatten (map (literals , s .args ))
305
+
306
+ def flatten (seqs ): return sum (seqs , [])
308
307
309
308
def variables (s ):
310
- """returns the set of variables in logical expression s.
309
+ """Return a set of the variables in expression s.
311
310
>>> ppset(variables(F(x, A, y)))
312
311
set([x, y])
313
312
>>> ppset(variables(expr('F(x, x) & G(x, y) & H(y, z) & R(A, z, z)')))
@@ -362,7 +361,7 @@ def parse_definite_clause(s):
362
361
#______________________________________________________________________________
363
362
364
363
def tt_entails (kb , alpha ):
365
- """Use truth tables to determine if KB entails sentence alpha . [Fig. 7.10]
364
+ """Does kb entail the sentence alpha? Use truth tables . [Fig. 7.10]
366
365
>>> tt_entails(expr('P & Q'), expr('Q'))
367
366
True
368
367
"""
0 commit comments