unintuitive for-loop behavior
Jussi Piitulainen
jussi.piitulainen at helsinki.fi
Sun Oct 2 04:38:31 EDT 2016
Steve D'Aprano writes:
> On Sun, 2 Oct 2016 11:44 am, Gregory Ewing wrote:
>
>> Steve D'Aprano wrote:
>>
>>> Certainly when you call a function, the local bindings need to be
>>> created. Obviously they didn't exist prior to calling the function!
>>> I didn't think that was the difference you were referring to, and I
>>> fail to see how it could be relevant to the question of for-loop
>>> behaviour.
>>
>> My proposed change is (mostly) equivalent to turning the
>> loop body into a thunk and passing the loop variable in as
>> a parameter.
>
> A thunk is not really well-defined in Python, because it doesn't
> exist, and therefore we don't know what properties it will have. But
> generally when people talk about thunks, they mean something like a
> light-weight anonymous function without any parameters:
>
> https://en.wikipedia.org/wiki/Thunk
According to a story (probably told in that page), thunks got their name
from being "already thunk of". Scheme at least doesn't have any special
"light-weight" anonymous functions, it just has (lambda () ...).
> You say "passing the loop variable in as a parameter" -- this doesn't
> make sense. Variables are not values in Python. You cannot pass in a
> *variable* -- you can pass in a name (the string 'x') or the *value*
> bound to the name, but there's no existing facility in Python to pass
> in a variable.
He means it like this:
def g4159(i):
<loop body here>
g4159(next(g4158))
The loop variable becomes the parameter of a function. The values are
passed to that function, one by one.
[snip tangent]
> hard to talk about your hypothetical change except in hand-wavy terms:
>
> "Something magically and undefined happens, which somehow gives the
> result I want."
The following is intended to be more concrete. You, Steve, have already
declared (in a followup to me in this thread) that it is not magical
(but is obfuscated, convoluted, non-idiomatic, and so on, but then you
had not understood that the expansion is not intended to be actual
source code).
So a Python for-loop *could* behave (*could have been defined to*
behave) so that
for v in s:
c
is equivalent to
try:
gs = iter(s)
while True:
def gf(v):
<nonlocal declarations>
c
gf(next(gs))
except StopIteration:
pass
where gs and gf are new names and <nonlocal declarations> make it so
that only v becomes local to gf.
>> This is the way for-loops or their equivalent are actually
>> implemented in Scheme, Ruby, Smalltalk and many other similar
>> languages, which is why they don't have the same "gotcha".
>
> Instead, they presumably have some other gotcha -- "why don't for
> loops work the same as unrolled loops?", perhaps.
I don't think so, but then I only know Scheme where tail recursion is
its own reward.
On the other hand, in Scheme I can actually implement any number of
different looping constructs if I find them desirable. Instead of
trying to explain that something is, in principle, not only possible but
also quite straightforward, I could just show an actual implementation.
(I did that once, but then it was a beginner who thought that some
really basic thing would be impossible merely because they were such a
beginner.)
> In Python, the most obvious gotcha would be that if for-loops
> introduced their own scope, you would have to declare any other
> variables in the outer scope nonlocal. [snip]
The intention is that the for-loop would own the loop variables. Other
variables would stay in the outer scope.
More information about the Python-list
mailing list