The problem with 'for' was that it was limited to stepping thru one
sequence. Now, with the introduction of "map( None, ... )" as a sort
of transpose operator, you can do:
result = []
for x,y,z in map( None, lista, listb, listc ):
result.append( x + y + z )
Not *quite* as succinct as:
result = map( lambda x,y,x: x+y+z, lista, listb, listc )
but more generally useful for some complicated operations. For
example, you can break out of the middle of a for loop or do a lot of
other conditional operations. ( and, as you point out - more
effecient! ) If some OTHER syntax for multi-list sequencing had been
introduced, we could have struggled along without map.
'apply( map, (func,)+args )' is still superior when the number of list
arguments is unknown. The same argument applies to 'apply' in
general: it ineffecient to call a function indirectly with apply, but
when you have an indefinite number of arguments, there's really no
alternative. I'm sure you wouldn't want to eliminate 'apply' and
resort to:
ln = len( args )
if ln == 1 : return f( args[0] )
elif ln = 2 : return f( args[0], args[1] )
...
If you use Python interactively ( especially with gnu-readline ) a lot,
( I do! ) you find the consiceness of packing a lot onto a single line
quite valuable. ( And I recall, it was Tim who suggested to me that
map + filter was the SOLUTION to my complaint that I couldn't put a
'for' and a conditional on one line together! )
> To be honest, I wish I hadn't introduced lambda, map, filter and
> reduce -- they support a style that is inconsistent with the rest of
> Python. Unfortunately, in the sake of backward compatibility, I can't
> take them out. So enjoy them if you have to. But don't get too
> thrilled!
I don't think they are any more inconsistent than 'apply' - and I
think that 'apply' and all of those functions are linked logically
and synergistically. Any one of them are stronger with the whole set
and weaker if any of them are removed.
I think the lambda scope problem DID raise a few questions, and, as
you pointed out, 'lambda' is the "weakest" of the set, in that you can
still do everything with 'def' at the cost of a bit more typing.
'lambda' is just a convenience, but it's such a BIG convenience! -
especially for interactive use, when you may not have planned things
out in advance.
With the exception of 'lambda', then, I'm not quite sure what you
could mean by the above. You don't mean to state that you think that
functions that take functions as arguments are somehow inconsistant
with Python ? I agree that Python functions were ALWAYS first class
functions before and without the addition of lambda - I thought that
WAS the intent ! Is functional programming anti-Python ?
Tim also once remarked that one of the things he liked about Python
was that it didn't force you into a particular style of programming.
Clearly, since this is following the thread on block syntax and
significant whitespace, I should stress that he was NOT talking
about cosmetic syntax. I don't object to THAT uniformity of style
that Python imposes - I happen to like that feature! But there is
a deeper sense of "style" : a style of problem solving - everyone's
toolkit of tricks that they have learned to rely upon, and more
generally, whole *paradigms* (or frameworks) of programming
like functional or Object Oriented programming. I think it would
be a mistake to exclude certain tools because you, personally, have
never learned to rely on them. It's fun for a language researcher
to take one particular paradigm and see how far he can push it -
scheme, Haskell, smalltalk, etc., and it's fun and mind expending
to try to learn the world view that goes with those languages,
but I'm not sure that any of them are the best "working" languages
for someone who just wants to get a job done.
As one more example of 'map', I'll append a piece of some recent
noodling with some remarks:
a = 440
interv = a / 12.0
scale = [ a ]
for i in range( 11 ):
scale.append( a + interv + (i * interv) )
scale = map( lambda x: int( x + 0.5 ), scale )
scale = scale + map( lambda x: 2*x, scale ) # append a second octave
notes = [ 'a', 'a#', 'b', 'c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#' ]
notes = notes + map( lambda s: string.upper(s), notes ) # 'A', 'A#' 'B' ...
[ The above could certainly have been done with for loops rather than
map, but I think the map version is more obvious and readable! ]
Scale = {} # and turn it into a dictionary
for a,b in map( None, notes, scale ):
Scale[a] = b
def notes( str ):
notelist = string.split( str )
Notes = []
for code in notelist:
Notes.append( Scale[code] )
return Notes
Mary_str = 'e d c d e e e d d d e g g'
Mary = notes( Mary_str )
t = [ 60, 20, 40, 40, 40, 40, 80, 40, 40, 80, 40, 40, 90 ]
[ But, though I hate to admit to being so musically inept - although
I got the notes right on the first try, it took me several tries to
get the time values right. It was a big help to be able to modify
't' and scroll back to this line to test it. ]
>>> HFT().play( map( None, t, Mary ) )
It takes 3 (IMHO) less clear lines to do the same thing.
( Well- the MOST clear would be to use 'lambda x,y: (x,y)' explicitly
instead of the 'None' convention, if you aren't familiar with it. )
out = []
for i in range( len( t ) ): out.append( t[i], Mary[i] )
HFT().play( out )
But, as I said in the 'for-with-multiple-lists' above, another
syntax to do the same thing:
[ (a,x), (b,y), (c,z) ] = [ a, b, c ] "some-operator" [ x, y, x ]
might well eliminate half of the 'map' usage and make Guido happy,
and I think it's a useful enough special case that it might deserve
it's own syntax. ( and get rid of the mysterious looking 'None' ! )
I just don't know what value for "some-operator" makes sense.
- Steve Majewski (804-982-0831) <sdm7g@Virginia.EDU>
- UVA Department of Molecular Physiology and Biological Physics