Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c89b517

Browse files
committed
Fixed issue 10. Fixed PropHornKB.retract. Cleanup.
1 parent 4bcde0b commit c89b517

File tree

1 file changed

+47
-50
lines changed

1 file changed

+47
-50
lines changed

logic.py

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -290,24 +290,23 @@ def is_definite_clause(s):
290290
>>> is_definite_clause(expr('(Farmer(f) | Rabbit(r)) ==> Hates(f, r)'))
291291
False
292292
"""
293-
if is_symbol(s.op): return True
294-
if s.op != '>>': return False
295-
antecedent, consequent = s.args
296-
antecedent = NaryExpr('&', antecedent)
297-
return (is_symbol(consequent.op)
298-
and (is_symbol(antecedent.op)
299-
or (antecedent.op == '&'
300-
and all(is_symbol(arg.op) for arg in antecedent.args))))
293+
if is_symbol(s.op):
294+
return True
295+
elif s.op == '>>':
296+
antecedent, consequent = s.args
297+
return (is_symbol(consequent.op)
298+
and all(is_symbol(arg.op) for arg in conjuncts(antecedent)))
299+
else:
300+
return False
301301

302302
def parse_definite_clause(s):
303303
"Return the antecedents and the consequent of a definite clause."
304304
assert is_definite_clause(s)
305305
if is_symbol(s.op):
306306
return [], s
307-
antecedent, consequent = s.args
308-
antecedent = NaryExpr('&', antecedent)
309-
antecedents = antecedent.args if antecedent.op == '&' else [antecedent]
310-
return antecedents, consequent
307+
else:
308+
antecedent, consequent = s.args
309+
return conjuncts(antecedent), consequent
311310

312311
## Useful constant Exprs used in examples and code:
313312
TRUE, FALSE, ZERO, ONE, TWO = map(Expr, ['TRUE', 'FALSE', 0, 1, 2])
@@ -462,8 +461,8 @@ def move_not_inwards(s):
462461
NOT = lambda b: move_not_inwards(~b)
463462
a = s.args[0]
464463
if a.op == '~': return move_not_inwards(a.args[0]) # ~~A ==> A
465-
if a.op =='&': return NaryExpr('|', *map(NOT, a.args))
466-
if a.op =='|': return NaryExpr('&', *map(NOT, a.args))
464+
if a.op =='&': return associate('|', map(NOT, a.args))
465+
if a.op =='|': return associate('&', map(NOT, a.args))
467466
return s
468467
elif is_symbol(s.op) or not s.args:
469468
return s
@@ -477,7 +476,7 @@ def distribute_and_over_or(s):
477476
((A | C) & (B | C))
478477
"""
479478
if s.op == '|':
480-
s = NaryExpr('|', *s.args)
479+
s = associate('|', s.args)
481480
if s.op != '|':
482481
return distribute_and_over_or(s)
483482
if len(s.args) == 0:
@@ -488,36 +487,43 @@ def distribute_and_over_or(s):
488487
if not conj:
489488
return s
490489
others = [a for a in s.args if a is not conj]
491-
rest = NaryExpr('|', *others)
492-
return NaryExpr('&', *[distribute_and_over_or(c|rest)
490+
rest = associate('|', others)
491+
return associate('&', [distribute_and_over_or(c|rest)
493492
for c in conj.args])
494493
elif s.op == '&':
495-
return NaryExpr('&', *map(distribute_and_over_or, s.args))
494+
return associate('&', map(distribute_and_over_or, s.args))
496495
else:
497496
return s
498497

499-
_NaryExprTable = {'&':TRUE, '|':FALSE, '+':ZERO, '*':ONE}
500-
501-
def NaryExpr(op, *args):
502-
"""Create an Expr, but with an nary, associative op, so we can promote
503-
nested instances of the same op up to the top level.
504-
>>> NaryExpr('&', (A&B),(B|C),(B&C))
498+
def associate(op, args):
499+
"""Given an associative op, return an expression with the same
500+
meaning as Expr(op, *args), but flattened -- that is, with nested
501+
instances of the same op promoted to the top level.
502+
>>> associate('&', [(A&B),(B|C),(B&C)])
505503
(A & B & (B | C) & B & C)
506-
>>> NaryExpr('|', A|(B|(C|(A&B))))
504+
>>> associate('|', [A|(B|(C|(A&B)))])
507505
(A | B | C | (A & B))
508506
"""
509-
arglist = []
507+
args = disassociate(op, args)
508+
if len(args) == 0:
509+
return _op_identity[op]
510+
elif len(args) == 1:
511+
return args[0]
512+
else:
513+
return Expr(op, *args)
514+
515+
_op_identity = {'&':TRUE, '|':FALSE, '+':ZERO, '*':ONE}
516+
517+
def disassociate(op, args):
518+
"""Given an associative op, return a flattened list result such
519+
that Expr(op, *result) means the same as Expr(op, *args)."""
520+
result = []
510521
def collect(subargs):
511522
for arg in subargs:
512523
if arg.op == op: collect(arg.args)
513-
else: arglist.append(arg)
524+
else: result.append(arg)
514525
collect(args)
515-
if len(arglist) == 1:
516-
return arglist[0]
517-
elif len(arglist) == 0:
518-
return _NaryExprTable[op]
519-
else:
520-
return Expr(op, *arglist)
526+
return result
521527

522528
def conjuncts(s):
523529
"""Return a list of the conjuncts in the sentence s.
@@ -526,10 +532,7 @@ def conjuncts(s):
526532
>>> conjuncts(A | B)
527533
[(A | B)]
528534
"""
529-
if isinstance(s, Expr) and s.op == '&':
530-
return s.args
531-
else:
532-
return [s]
535+
return disassociate('&', [s])
533536

534537
def disjuncts(s):
535538
"""Return a list of the disjuncts in the sentence s.
@@ -538,10 +541,7 @@ def disjuncts(s):
538541
>>> disjuncts(A & B)
539542
[(A & B)]
540543
"""
541-
if isinstance(s, Expr) and s.op == '|':
542-
return s.args
543-
else:
544-
return [s]
544+
return disassociate('|', [s])
545545

546546
#______________________________________________________________________________
547547

@@ -574,18 +574,19 @@ def pl_resolve(ci, cj):
574574
if di == ~dj or ~di == dj:
575575
dnew = unique(removeall(di, disjuncts(ci)) +
576576
removeall(dj, disjuncts(cj)))
577-
clauses.append(NaryExpr('|', *dnew))
577+
clauses.append(associate('|', dnew))
578578
return clauses
579579

580580
#______________________________________________________________________________
581581

582582
class PropHornKB(PropKB):
583583
"A KB of propositional Horn clauses."
584+
# Actually definite clauses, but I won't resolve this discrepancy till
585+
# the code upgrade to the 3rd edition.
584586

585587
def tell(self, sentence):
586588
"Add a Horn clause to this KB."
587-
op = sentence.op
588-
assert op == '>>' or is_prop_symbol(op), "Must be Horn clause" # XXX use is_definite_clause?
589+
assert is_definite_clause(sentence), "Must be definite clause"
589590
self.clauses.append(sentence)
590591

591592
def ask_generator(self, query):
@@ -594,23 +595,19 @@ def ask_generator(self, query):
594595
yield {}
595596

596597
def retract(self, sentence):
597-
"Remove the sentence's clauses from the KB"
598-
for c in conjuncts(to_cnf(sentence)):
599-
if c in self.clauses:
600-
self.clauses.remove(c)
598+
self.clauses.remove(sentence)
601599

602600
def clauses_with_premise(self, p):
603601
"""Return a list of the clauses in KB that have p in their premise.
604602
This could be cached away for O(1) speed, but we'll recompute it."""
605603
return [c for c in self.clauses
606-
if c.op == '>>' and p in conjuncts(c.args[0])] # XXX use parse_definite_clause?
604+
if c.op == '>>' and p in conjuncts(c.args[0])]
607605

608606
def pl_fc_entails(KB, q):
609607
"""Use forward chaining to see if a HornKB entails symbol q. [Fig. 7.14]
610608
>>> pl_fc_entails(Fig[7,15], expr('Q'))
611609
True
612610
"""
613-
# XXX use parse_definite_clause?
614611
count = dict([(c, len(conjuncts(c.args[0]))) for c in KB.clauses
615612
if c.op == '>>'])
616613
inferred = DefaultDict(False)

0 commit comments

Comments
 (0)