Why is Python popular, while Lisp and Scheme aren't?
Tim Hochberg
tim.hochberg at ieee.org
Thu Nov 21 13:08:52 EST 2002
"Alex Martelli" <aleax at aleax.it> wrote in message
news:5r6D9.50977$Yw.2347638 at news2.tin.it...
> Patrick W wrote:
>
> > Alex Martelli <aleax at aleax.it> writes:
[Nice neat solution for rock-paper-scissors]
[More complicated solution for more extensible rock-paper-scissors]
> so I could somebeat.addpair(aclass, another) to mean than an instance
> of aclass 'beats' one of another unconditionally (when evaluated by
> the criteria in somebeat), or somebeat.addpair(Ba, Bo, pp) to mean
> that for instances of Ba and Bo the 'beating' or otherwise is to be
> decided by calling pp (which must be callable, to mean that:-) with
> the instances as arguments. And once again that's a small further
> notch in complication which I'd be paying for a flexibility gain.
>
> So, I do miss multiple dispatching, which would let me avoid this
> infrastructure building just like I can avoid it in the more common
> single dispatch case.
>
> But I don't miss it ENOUGH to wish that Python had macros (shudder)...:-).
> Not by a long shot, in fact...
The interesting thing is that as far as I know, no one has put together a
module to simulate multiple dispatch. Or if they have, they haven't pushed
it very hard. I don't think it would be all that hard really, just a tad
more complicated than what you had above. Something similar to what I have
below would probably work, although the performance might not be the best.
Once you have a Dispatcher class (like the one below only better
presumably), writing multiple dispatch functions would be a piece of cake.
-tim
# Rock-Paper-Scissors as an excuse to simulate multiple dispatch.
def argsmatchsig(args, sig):
if len(args) != len(sig):
return 0
for a, s in zip(args, sig):
if not isinstance(a, s):
return 0
return 1
class Dispatcher:
def __init__(self, table=()):
self.table = list(table)
def __call__(self, *args):
"""Try rules in order, calling the first matching rule"""
for rule, func in self.table:
if argsmatchsig(args, rule):
return func(*args)
raise TypeError("%s instance cannot be called with args types %s" %
(self.__class__.__name__,
tuple([type(x) for x in args])))
def add_rule(self, sig, func):
"""Add new rule -- the new rule is tried before all existing
rules"""
self.table.insert(0, (sig, func))
def remove_rule(self, sig):
"""Remove the first rule with the given signature."""
for s, f in self.table:
if s == sig:
self.table.remove((s, f))
class Thing(object): pass
class Rock(Thing): pass
class Paper(Thing): pass
class Scissors(Thing): pass
rock, paper, scissors = Rock(), Paper(), Scissors()
def return_true(*args):
return 1
def return_false(*args):
return 0
beats = Dispatcher([((Rock, Scissors), return_true),
((Scissors, Paper), return_true),
((Paper, Rock), return_true),
((Thing, Thing), return_false)])
print beats(rock, scissors) # prints 1
print beats(paper, scissors) # prints 0
class Fire(Thing): pass
fire = Fire()
beats.add_rule((Fire, Thing), lambda a, b : "Fire always wins!")
print beats(fire, rock) # prints "Fire always wins!"
print beats(fire, paper) # prints "Fire always wins!"
print beats(scissors, rock) # prints 0
beats.remove_rule((Fire, Thing))
print beats(fire, rock) # prints 0
print beats(fire, paper) # prints 0
print beats(scissors, rock) # prints 0
print beats(scissors, "") # Raises a TypeError
More information about the Python-list
mailing list