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