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