<div style="line-height:1.7;color:#000000;font-size:14px;font-family:Arial"><div>Hi Steve, Thank you for so detailed comments.<br>My comments also below interleaved with yours.<br></div><br><div style="position:relative;zoom:1"></div><div id="divNeteaseMailCard"></div><br><pre><br>At 2018-02-16 08:57:40, "Steven D'Aprano" <steve@pearwood.info> wrote:
>Hi fhsxfhsx, and welcome.
>
>My comments below, interleaved with yours.
>
>
>On Thu, Feb 15, 2018 at 01:56:44PM +0800, fhsxfhsx wrote:
>
>[quoted out of order]
>> And I hope the discussion could focus more on whether we should allow
>> assigning temporary variables in comprehensions rather than how to
>> solve the specific example I mentioned above.
>
>Whether or not to allow this proposal will depend on what alternate
>solutions to the problem already exist, so your specific example is very
>relevant. Any proposed change has to compete with existing solutions.
>>
>> As far as I can see, a comprehension like
>> alist = [f(x) for x in range(10)]
>> is better than a for-loop
>> for x in range(10):
>> alist.append(f(x))
>> because the previous one shows every element of the list explicitly so
>> that we don't need to handle `append` mentally.
>
>While I personally agree with you, many others disagree. I know quite a
>few experienced, competent Python programmers who avoid list
>comprehensions because they consider them harder to read and reason
>about. They consider a regular for-loop better precisely because you do
>see the explicit call to append.
>
>(In my experience, those of us who get functional-programming idioms
>often forget that others find them tricky.)
>
>The point is that list comprehensions are already complex enough that
>they are difficult for many people to learn, and some people never come
>to grips with them. Adding even more features comes with a cost.
>
>The bottom line is that it isn't clear to me that allowing local
>variables inside comprehensions will make them more readable.
><div><pre><br>To be frank, I had not thought of this before.<br>However, in my opinion, when considering adding a new syntax, we care more about the marginal cost.<br>I mean, I think it is the functional-programming way which is tricky, but allowing a new syntax would not make things worse.<br>Well, that's just a guess, maybe only those who are puzzled with comprehensions can give us an answer.<br><br></pre></div>>
>> But when it comes to something like
>> [f(x) + g(f(x)) for x in range(10)]
>> you find you have to sacrifice some readableness if you don't want two
>> f(x) which might slow down your code.
>
>The usual comments about premature optimisation apply here.
>
>Setting a new comprehension variable is not likely to be free, and may even be
>more costly than calling f(x) twice if f() is a cheap expression:
>
> [x+1 + some_func(x+1) for x in range(10)]
>
>could be faster than
>
> [y + some_func(y) for x in range(10) let y = x + 1]
>
>or whatever syntax we come up with.
><div><pre>It is true. But since there are still so many cases where a temporary variable is faster. <br>Also, even without let-clause, one can write a for-loop with a temporary variable which slow down the code.<br>So, it seems that "setting a new comprehension variable may even be more costly" does not show any uselessness of temporary variables in comprehensions.<br><br></pre></div>>
>> Someone may argue that one can write
>> [y + g(y) for y in [f(x) for x in range(10)]]
>
>Indeed. This would be the functional-programming solution, and I
>personally think it is an excellent one. The only changes are that I'd
>use a generator expression for the intermediate value, avoiding the need
>to make a full list, and I would lay it out more nicely, using
>whitespace to make the structure more clear:
>
> result = [y + g(y) for y in
> (f(x) for x in range(10))
> ]
></pre><div><br>In my opinion, </div><pre><div>[<br> y + g(y)<br> for x in range(10)<br> let y = f(x)<br>]<br>is better because it's more corresponding to a for-loop<br>for x in range(10):<br> y = f(x)<br> result.append(y + g(y))<br>In my opinion, comprehensions are not real functional-programming because there is not even a function. Though there're similarities, map and filter are real functional-programming. Since the similarity between for-clause in comprehensions and the for-loop, I think it's better to write comprehensions more close to for-loop.<br>I don't know but I guess maybe it can also help those who fear comprehensions better embrace them?<br><br></div>>
>> but it's not as clear as to show what `y` is in a subsequent clause,
>> not to say there'll be another temporary list built in the process.
>
>There's no need to build the temporary list. Use a generator
>comprehension. And I disagree that the value of y isn't as clear.
>
>An alternative is simply to refactor your list comprehension. Move the
>calls to f() and g() into a helper function:
>
>def func(x):
> y = f(x)
> return y + g(y)
>
>and now you can write the extremely clear comprehension
>
>[func(x) for x in range(10)]
>
>that needs no extra variable.
><div><pre><pre><pre>I think it can be a goods idea if there's a name to `func` which is easy to understand, or `func` is put close to the comprehension and is in a obvious place.<br>But I feel it's not for the case I gave in another mail to Paul, <a href="https://mail.python.org/pipermail/python-ideas/2018-February/048997.html," _src="https://mail.python.org/pipermail/python-ideas/2018-February/048997.html,">https://mail.python.org/pipermail/python-ideas/2018-February/048997.html,</a><br>(I'm sorry that the example is quite long, and I don't hope to copy it here) <br>To me, it can be confusing to have several `func` when I have several lists at the same time and have to transform them each in a similar but different way.<br><br></pre></pre></pre></div>>
>
>[...]
>> In a word, what I'm arguing is that we need a way to assign temporary
>> variables in a comprehension.
>
>"Need" is very strong. I think that the two alternatives I mention above
>cover 95% of the cases where might use a local variable in a
>comprehension. And of the remaining cases, many of them will be so
>complex that they should be re-written as an explicit for-loop. So in my
>opinion, we're only talking about a "need" to solve the problem for a
>small proportion of cases:
>
>- most comprehensions don't need a local variable (apart from
> the loop variable) at all;
>
>- of those which do need a local variable, most can be easily
> solved using a nested comprehension or a helper function;
>
>- of those which cannot be solved that way, most are complicated
> enough that they should use a regular for-loop;
>
>- leaving only a small number of cases which are complicated enough
> to genuinely benefit from local variables but not too complicated.
>
>So this is very much a borderline feature. Occasionally it would be
>"nice to have", but on the negative side:
>
>- it adds complexity to the language;
>
>- makes comprehensions harder to read;
>
>- and people will use it unnecessarily where there is no readability
> or speed benefit (premature optimization again).
>
>It is not clear to me that we should burden *all* Python programmers
>with additional syntax and complexity of an *already complex* feature
>for such a marginal improvement.
>
>
>> In my opinion, code like
>> [y + g(y) for x in range(10) **some syntax for `y=f(x)` here**]
>> is more natural than any solution we now have.
>> And that's why I pro the new syntax, it's clear, explicit and readable
>
>How can you say that the new syntax is "clear, explicit and readable"
>when you haven't proposed any new syntax yet?
>
>For lack of anything better, I'm going to suggest "let y = f(x)" as the
>syntax, although personally I don't like it even a bit.
>
>Where should the assignment go?
>
> [(y, y**2) let y = x+1 for x in (1, 2, 3, 4)]
>
> [(y, y**2) for x in (1, 2, 3, 4) let y = x+1]
>
>I think they're both pretty ugly, but I can't think of anything else.
>
>Can we rename the loop variable, or is that an error?
>
> [(x, x**2) let x = x+1 for x in (1, 2, 3, 4)]
>
>How do they interact when you have multiple loops and if-clauses?
>
> [(w, w**2) for x in (1, 2, 3, 4) let y = x+1
> for a in range(y) let z = a+1 if z > 2
> for b in range(z) let w = z+1]
>
>
>For simplicity, perhaps we should limit any such local assignments to
>the very end of the comprehension:
>
> [expression for name in sequence
> <zero or more for-loops and if-clauses>
> <zero or more let-clauses>
> ]
>
>but that means we can't optimise this sort of comprehension:
>
> [expression for x in sequence
> for y in (something_expensive(x) + function(something_expensive(x))
> ]
>
>Or these:
>
> [expression for x in sequence
> if something_expensive(x) or condition(something_expensive(x))
> ]
>
>
>I think these are very hard questions to answer.
><div>I think the assignment should be treated equally as for-clause and if-clause which means<br><pre>[(y, y**2) for x in (1, 2, 3, 4) let y = x+1]<br>would be a right syntax.<br>And <br>[(x, x**2) for x in (1, 2, 3, 4) let x = x+1]</pre></div><div>would not cause an error because we can also write<br>[(x, x**2) for x in (1, 2, 3, 4) for x in (4, 3, 2, 1)]<br>now.<br><br>I didn't see any problem in <br><pre>[(w, w**2) for x in (1, 2, 3, 4) let y = x+1
for a in range(y) let z = a+1 if z > 2
for b in range(z) let w = z+1]<br><br>In my opinion, it would behave the same as<br>for x in (1, 2, 3, 4):<br> y = x+1<br> for a in range(y):<br> z = a+1<br> if z > 2:<br> for b in range(z):<br> w = z+1<br> mylist.append((w, w**2))<br></pre>According to my understanding, the present for-clause and if-clause does everything quite similar to this nested way,<br><br></div>
>
>--
>Steve
>_______________________________________________
>Python-ideas mailing list
>Python-ideas@python.org
>https://mail.python.org/mailman/listinfo/python-ideas
>Code of Conduct: http://python.org/psf/codeofconduct/
</pre></div><br><br><span title="neteasefooter"><p> </p></span>