def add(n):
yield n
for m in add(n+1):
yield m
With recursive generators, it’s easy to build complex backtrackers:
def resolve(db, goals, cut_parent=0):
try:
head, tail = goals[0], goals[1:]
except IndexError:
yield {}
return
try:
predicate = (
deepcopy(clause)
for clause in db[head.name]
if len(clause) == len(head)
)
except KeyError:
return
trail = []
for clause in predicate:
try:
unify(head, clause, trail)
for each in resolve(db, clause.body, cut_parent + 1):
for each in resolve(db, tail, cut_parent):
yield head.subst
except UnificationFailed:
continue
except Cut, cut:
if cut.parent == cut_parent:
raise
break
finally:
restore(trail)
else:
if is_cut(head):
raise Cut(cut_parent)
...
for substitutions in resolve(db, query):
print substitutions
This is a Prolog engine implemented by a recursive generator. db is a dict representing the Prolog database of facts and rules. unify () is a unification function that creates all the substitutions for the current target and adds changes to the trace, so you can undo them later. restore () cancels and is_cut () if the current target is "!", so we can trim the branches.