lazy (?) evaluation, request for advice

Alex Martelli alex at magenta.com
Wed Jan 5 18:16:52 EST 2000


I'm planning to prototype in Python a bunch of bridge-related
functionality, including selective dealing.  In selective dealing,
the program must repeatedly generate random deals, and
apply on each generated deal a user-supplied function, which
can accept or reject the deal depending on whether it matches
the characteristics the user is trying to select for -- this is very
useful for practice, analysis, and statistical studies, and
several custom programs (as well as a set of extensions to
Tcl) exist for the purpose... but I'd much rather let the user
write the deal-selection function in Python, than in either Tcl,
or any custom, ad-hoc "little language".

The number of features of a bridge deal that could be
selected for is huge, and some of them are pretty costly
to compute (for the huge number of deals that are often
needed when one is selecting for rare combinations
and/or wants a lot of selected hands, in order to collect
statistics about them).  So, I would like to compute only
those features that the user actually needs on a given run.

Further, since the typical user will not be a programmer,
I would like to make the user's life as easy as possible.
I.e., despite the important mantra about explicit being
better than implicit, I think the user would much rather
be able to write, say:
    (spades>=6 or hearts>=6) and (3.0<losers<4.0)
rather than have to mention "current_hand.spades" or
other such explicit notation.


If I didn't need lazy evaluation, I could easily provide
the latter nice-for-the-user syntax, e.g.:

    def eval_hand(a_hand):
        d={}
        d['spades']= # compute spade length of a_hand
        # ditto for 'hearts','diamonds','clubs'
        d['losers']= # compute a_hand's losers
        # ditto for umpteen other criteria
        return d

    for a_hand in some_huge_sequence:
        if(eval(user_expression,eval_hand(a_hand))):
            # add a_hand to those being selected


Or, if I were willing to make the user write "x.spades",
etc, instead of just "spades", I could do lazy evaluation
by using as 'x' an object with a suitable __getattr__.


I've thought about doing the eval repeatedly, within
a try/except block to catch the NameError's, inserting
the missing names in the dictionary one by one, but
I'm suspicious about the performance characteristics
of such an approach.  Maybe it would be all right if,
for hands after the first one, I pre-inserted values for
the names that are already in the dictionary from
previous iterations -- that would swiftly reduce the
number of NameError's to nil on most iterations,
although it would mean I would often have to compute
more features than necessary (e.g., for the above
example expression, I would be doing the costly
computation of losers for every hand, a pity since
it's only actually needed for the rare hands that have
at least 6 spades or hearts).


Of course, if I could use a UserDict rather than a
real dictionary in the eval(), I'd be in clover, but I
can't -- that gives a TypeError.


So... once more, this newbie is looking for advice
and counsel from you kindly experts!  What do you
think is the best approach for this situation...?
Thanks in advance, of course!


Alex






More information about the Python-list mailing list