If that was second-best, what is first?

Contents

Peter Norvig stated that giving the `c` function attributes for `c.starts` and `c.items` was his "second-best" solution. He shares his preferred solution here.

Notice that `c(orderings)` has to be used in 5 different places with the second-best (function attribute-based) solution, while `c` is used in only one place with the better (class-based) solution.

``````class c(object):
"""Convert a sequence to an iterable that counts accesses to it.
At any time you can ask the object for .starts (the number of iters
that have started) and .items (the total number of items iterated)."""

def __init__(self, sequence):
self.sequence = list(sequence)
self.starts, self.items = 0, 0

def __iter__(self):
self.starts += 1
for item in self.sequence:
self.items += 1
yield item

houses = first, _, middle, _, _ = [1, 2, 3, 4, 5]
orderings = c(itertools.permutations(houses))
def zebra_puzzle():
"Return a tuple (WATER, ZEBRA indicating their house numbers."
return next((WATER, ZEBRA)
for (red, green, ivory, yellow, blue) in orderings
if imright(green, ivory)
for (Englishman, Spaniard, Ukranian, Japanese, Norwegian) in orderings
if Englishman is red
if Norwegian is first
if nextto(Norwegian, blue)
for (coffee, tea, milk, oj, WATER) in orderings
if coffee is green
if Ukranian is tea
if milk is middle
for (OldGold, Kools, Chesterfields, LuckyStrike, Parliaments) in orderings
if Kools is yellow
if LuckyStrike is oj
if Japanese is Parliaments
for (dog, snails, fox, horse, ZEBRA) in orderings
if Spaniard is dog
if OldGold is snails
if nextto(Chesterfields, fox)
if nextto(Kools, horse)
)

def instrument_fn(fn, *args):
result = fn(*args)
print('%s got %s with %5d iters over %7d items'%(
fn.__name__, result, orderings.starts, orderings.items))
``````

``````def zebra_puzzle():
"Return a tuple (WATER, ZEBRA indicating their house numbers."
houses = first, _, middle, _, _ = [1, 2, 3, 4, 5]
orderings = list(itertools.permutations(houses)) # 1
return next((WATER, ZEBRA)
for (red, green, ivory, yellow, blue) in c(orderings)
if imright(green, ivory)
for (Englishman, Spaniard, Ukranian, Japanese, Norwegian) in c(orderings)
if Englishman is red
if Norwegian is first
if nextto(Norwegian, blue)
for (coffee, tea, milk, oj, WATER) in c(orderings)
if coffee is green
if Ukranian is tea
if milk is middle
for (OldGold, Kools, Chesterfields, LuckyStrike, Parliaments) in c(orderings)
if Kools is yellow
if LuckyStrike is oj
if Japanese is Parliaments
for (dog, snails, fox, horse, ZEBRA) in c(orderings)
if Spaniard is dog
if OldGold is snails
if nextto(Chesterfields, fox)
if nextto(Kools, horse)
)

def c(sequence):
c.starts += 1
for item in sequence:
c.items += 1
yield item

def instrument_fn(fn, *args):
c.starts, c.items = 0, 0
result = fn(*args)
print('%s got %s with %5d iters over %7d items'%(
fn.__name__, result, c.starts, c.items))
``````