python hack of the day -- "listable" functions

Stefan Franke spamfranke at bigfoot.de
Thu May 13 18:50:34 EDT 1999


On Thu, 13 May 1999 20:15:32 GMT, wtanksle at dolphin.openprojects.net (William
Tanksley) wrote:

>On Thu, 13 May 1999 19:50:35 GMT, Stefan Franke wrote:
>>On 12 May 1999 16:30:34 -0700, Michael Vanier <mvanier at bbb.caltech.edu> wrote:
>
>>but that's only syntactic sugar. Imagine an 'operator' |..|, which marks a
>>sequence argument in an expression and maps the whole expression to it.
[...]
>>Of, course a syntactic form like this would be completely unsuitable for a
>>programming language, because |...| is not an operator but some kind of macro
>>facility (try to think of rules for combinations of several |...| ).
>
>This isn't true -- there are languages which do exactly this.  I think
>Icon is one, and I know APL, J, and K are. 

Yes, you're right. I was a litle too quick when I stated "completely
unsuitable". What I wanted to say is that it doesn't fit into the normal
function appliation scheme, since it is - like you said -

>[...] it's a modification of the way a function operates.  In the above 
>example, we're forcing '*' to operate on the members of the list, 
> we're not changing the list.  

I think function application with $ does follow the rewrite rule

    f(x, y, $[z1, z2, ..., zn], a) ->
   $[f(x, y, z1, a), f(x, y, z2, a), ..., f(x, y, zn, a)]

I think an Implementation in Python itself is not possible. However, it could be
done with some minimal support on a meta object protocol level. Assume 

1) we had an aditional builtin type 'dollar', so we could write
>>> x = $[1, 2, 3, 4]
>>> type(x)
<type 'dollar'>
>>> x
$[1, 2, 3, 4]
>>> 2*x+1
$[3, 5, 7, 9]
>>> undollar (x)
[1, 2, 3, 4]

2) Python had a hook that allows us to customize function application. On each
function call - to builtin as well as user functions - the function 'apply' is
called. Assigning to apply allows us to change the interpreter behaviour
completely. So we could do

    __apply__ = apply
    apply = my_apply

and implement the above rewrite rule (sorry, code not tested  ;-) with :

# Leaving keyword arguments out of consideration
def my_apply(f, *args):
    # Look for the first dollared argument, if there is any
    i=0
    for arg in args:
        if type(arg) is types.DollarType:
            # Dollared argument found at index i
            seq = undollar(arg)
            # Build argument sequence for the mapping on seq's elements
            arg_seq = map (lambda x, args=args, i=i: args[:i] +(x,) +args[i+1:],
                           seq)
            # On each f(as) call, the whole my_apply machinery gets applied
            # recursivley, if there are any following dollared arguments in
            # args behind index i
            return $map (lambda as, f=f: f(as), arg_seq)
        i=i+1
    else:
        # Apply f on args with the builtin apply 
        return __apply__(__apply__, ((f,) +args))

Of course, each function call in my_apply calls my_apply recursively, but only
once, since there are no dollared arguments involved.

They'll-propably-stone-us-for-using-$-in-Python-even-if-it's-only-an-illustration-ly
yr's

Stefan

PS: I love the abstraction level of these operations. I never had a look at APL,
J or K before, so I guess I have to.

PPS: undollar is the only function that isn't mapped on the sequence. This
special case I left out in my_apply.





More information about the Python-list mailing list