unintuitive for-loop behavior

Rustom Mody rustompmody at gmail.com
Sat Oct 1 12:57:03 EDT 2016


On Saturday, October 1, 2016 at 7:02:58 PM UTC+5:30, Jussi Piitulainen wrote:
> Rustom Mody writes:
> 
> > And then define comprehensions not as now done in terms of for loops
> > that mutatingly extend the list being built up but as recursive
> > functions that get (re)called for every new value of the comprehension
> > variable passed and therefore fresh-bound as parameter
> 
> You'd get the magical semantics for comprehensions from the current
> definition of comprehension semantics in terms of the loop, if the loop
> semantics was magical. Let me demonstrate. (The b-word is unfortunately
> not available, so I substitute "magical" instead.)
> 
> # delayd = [ lambda : c for c in "abracadabra" ]
> # is roughly/almost equal to the following.
> 
> delayd = []
> for c in "abracadabra":
>     delayd.append(lambda : c)
> 
> print('Python:', ''.join(f() for f in delayd))
> 
> # But that for-loop could have been roughly equal to the following,
> # giving both the comprehension and the underlying for-loop a
> # semantics that some people say they would prefer.
> 
> delayd = [] # reset the list, not part of the loop
> 
> g1 = iter("abracadabra")
> try:
>     while True:
>         def g2(c):
>             delayd.append(lambda : c)
>         g2(next(g1))
> except StopIteration:
>     pass
> 
> print('Magick:', ''.join(f() for f in delayd))
> 
> # Output from the above:
> # Python: aaaaaaaaaaa
> # Magick: abracadabra

Hoo boy1
Thats some tour de force and makes my head spin

Point can be made more simply with map
ie if we *define*
[exp for cv in l]
as
map(lambda cv: exp, l)

the problem vanishes

Demo:

First a helper function for demoing:

def pam(fl,x):
    return map(lambda f: f(x), fl)
# pam is the complement to map; map runs one fnc on a list of args
# pam runs a list of funcs on one arg

Trying to make a list of functions that add one, two and three to their arguments

fl = [lambda x: x + cv for cv in [1,2,3]]

Broken because of python's wrong LC semantics:
>>> pam(fl, 3)
[6, 6, 6]

Transform the LC into a map with the rule above:
fl_good = map((lambda cv :lambda x: x+cv), [1,2,3])

Works!
>>> pam(fl_good, 3)
[4, 5, 6]
>>> 

Which is not very far from the standard workaround for this gotcha:
>>> fl_workaround = [lambda x, cv=cv: x+cv for cv in [1,2,3]]
>>> pam(fl_workaround, 3)
[4, 5, 6]
>>> 

Maybe we could say the workaround is the map definition uncurried
And then re-comprehension-ified



More information about the Python-list mailing list