[Python-ideas] Temporary variables in comprehensions
Chris Barker - NOAA Federal
chris.barker at noaa.gov
Thu Feb 15 20:37:33 EST 2018
I’d like to clarify that f(x) was indeed meant to be a sequence. As per
monad law:
*do* { y *<-* *do* { x *<-* m;
f x
}
g y
}
===
*do* { x *<-* m;
y *<-* f x;
g y
}
I think you might have misunderstood the types of things; f: Function[a,
List[b]] and g: Function[b, List[c]]. m: List[a]
I’m still confused — f and g take a scale and return a list? Or take two
parameters, a scalar and a list???
Either way, doesn’t really fit with python comprehensions, which are more
or less expecting functions that except and return a single element (which
could be a triple, but I digress)
And the key point is that in python:
for x in seq1 for y in seq2
Does all combinations, like a nested for loop would.
But I DO think my old the fly write up went wrong, converting do-notation
to list comprehensions isn’t completely straightforward
Well, I don’t think I get the do notation at all....
The above is equivalent to
[g y | x <- m, y <- f x] in Haskell and the top is [g y | y <- [z |x <- m,
z <- f x]]
These have analogous structures in python;
[g(y) for x in m for y in f(x)] and [g(y) for y in [z for x in m for z in
f(x)]] (I think?)
Is this current Python or a new proposed syntax — ‘cause I can’t make any
sense of it in python.
Maybe define some f,g,m that behave as you expect, and see what python
does....
If I’ve not made another mistake, that **should* *now work?
Now on a phone, so can’t test, but I also can’t tell what you are expecting
the result to be.
Back to one of your examples:
[f(x) for x in [x]]
What does that mean???
for x in seq
Means iterate through seq, and assign each item to the name x.
If that seq has x in it — I’m not sure that is even legal python — the
scope in a comprehension confuses me.
But that is the equivalent is something like:
it= iter(seq)
while True:
Try:
x = next(it)
Except StopIteration:
Break
(Excuse the caps — hard to write code on a phone)
So not sure how x gets into that sequence before the loop starts.
-CHB
*From:* Chris Barker [mailto:chris.barker at noaa.gov <chris.barker at noaa.gov>]
*Sent:* 15 February 2018 23:34
*To:* jw14896.2014 at my.bristol.ac.uk
*Cc:* Evpok Padding <evpok.padding at gmail.com>; Python-Ideas <
python-ideas at python.org>
*Subject:* Re: [Python-ideas] Temporary variables in comprehensions
On Thu, Feb 15, 2018 at 2:13 AM, Jamie Willis <jw14896.2014 at my.bristol.ac.uk>
wrote:
These laws define behaviour that is expected equivalent by users;
[x for x in xs] = xs
OK -- that's the definition...
[f(x) for x in [x]] = f(x)
well, not quite:
[f(x) for x in [x]] = [f(x)]
Using x in two places where they mean different things makes this odd, but
yes, again the definition (of a list comp, and a length-1 sequence)
[g(y) for y in [f(x) for x in xs]] = [g(y) for x in xs for y in f(x)]
well, no. using two for expressions yields the outer product -- all
combinations:
In [*14*]: xs = range(3)
In [*15*]: [(x,y) *for* x *in* xs *for* y *in* xs]
Out[*15*]: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1),
(2, 2)]
so the result is a length len(seq1)*len(seq(2)) list. Or in this case, a
len(xs)**2.
But nesting the comps applies one expression, and then the other, yielding
a length len(xs) list.
but you wrote:
[g(y) for x in xs for y in f(x)]
which I'm not sure what you were expecting, as f(x) is not a sequence
(probably)...
To play with your examples:
Define some functions that make it clear what's been applied:
In [*16*]: *def* f(x):
...: *return* "f(*{}*)".format(x)
...:
In [*17*]: *def* g(x):
...: *return* "g(*{}*)".format(x)
and a simple sequence to use:
In [*18*]: xs = range(3)
Now your examples:
In [*19*]: [x *for* x *in* xs]
Out[*19*]: [0, 1, 2]
In [*20*]: [f(x) *for* x *in* [x]]
Out[*20*]: ['f(5)']
In [*21*]: [g(y) *for* y *in* [f(x) *for* x *in* xs]]
Out[*21*]: ['g(f(0))', 'g(f(1))', 'g(f(2))']
OK -- all good f applied, then g, but then the last one:
In [*27*]: [g(y) *for* x *in* xs *for* y *in* f(x)]
Out[*27*]:
['g(f)',
'g(()',
'g(0)',
'g())',
'g(f)',
'g(()',
'g(1)',
'g())',
'g(f)',
'g(()',
'g(2)',
'g())']
in this case, f(x) is returning a string, which is a sequence, so you get
that kind odd result. But what if f(x) was a simple scalr function:
In [*29*]: *def* f(x):
...: *return* 2*x
Then you just get an error:
In [*30*]: [g(y) *for* x *in* xs *for* y *in* f(x)]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-30-82ba3864654b> in <module>()
----> 1 [g(y) for x in xs for y in f(x)]
<ipython-input-30-82ba3864654b> in <listcomp>(.0)
----> 1 [g(y) for x in xs for y in f(x)]
TypeError: 'int' object is not iterable
The the nested comp is what is desired here:
In [*31*]: [g(y) *for* y *in* [f(x) *for* x *in* xs]]
Out[*31*]: ['g(0)', 'g(2)', 'g(4)']
Except you probably want a generator expression in the inner loop to avoid
bulding an extra list:
In [*33*]: [g(y) *for* y *in* (f(x) *for* x *in* xs)]
Out[*33*]: ['g(f(0))', 'g(f(1))', 'g(f(2))']
So back to the OP's example:
In [*34*]: [f(x) + g(f(x)) *for* x *in* range(10)]
Out[*34*]:
['f(0)g(f(0))',
'f(1)g(f(1))',
'f(2)g(f(2))',
'f(3)g(f(3))',
'f(4)g(f(4))',
'f(5)g(f(5))',
'f(6)g(f(6))',
'f(7)g(f(7))',
'f(8)g(f(8))',
'f(9)g(f(9))']
that is best done with comps as:
In [*36*]: [fx + g(fx) *for* fx *in* (f(x) *for* x *in* range(10))]
Out[*36*]:
['f(0)g(f(0))',
'f(1)g(f(1))',
'f(2)g(f(2))',
'f(3)g(f(3))',
'f(4)g(f(4))',
'f(5)g(f(5))',
'f(6)g(f(6))',
'f(7)g(f(7))',
'f(8)g(f(8))',
'f(9)g(f(9))']
which really doesn't seem bad to me. And if the function names are longer
-- which they should be, you might want to use a temp as suggested earlier:
In [*41*]: fx = (f(x) *for* x *in* range(10))
In [*42*]: [x + g(x) *for* x *in* fx]
Out[*42*]:
['f(0)g(f(0))',
'f(1)g(f(1))',
'f(2)g(f(2))',
'f(3)g(f(3))',
'f(4)g(f(4))',
'f(5)g(f(5))',
'f(6)g(f(6))',
'f(7)g(f(7))',
'f(8)g(f(8))',
'f(9)g(f(9))']
The truth is, comprehensions really are a bit wordy, if you are doing a lot
of this kind of thing (at least with numbers), you might be happier with an
array-oriented language or library, such as numpy:
In [*46*]: *import* *numpy* *as* *np*
In [*47*]: *def* f(x):
...: *return* x * 2
...:
In [*48*]: *def* g(x):
...: *return* x * 3
...:
...:
In [*49*]: xs = np.arange(3)
In [*50*]: f(xs) + g(f(xs))
Out[*50*]: array([ 0, 8, 16])
is pretty compact, and can be "optimized with a temp:
In [*51*]: fx = f(xs)
...: fx + g(fx)
...:
Out[*51*]: array([ 0, 8, 16])
pretty simple isn't it?
So this gets back to -- does anyone have a suggestion for a syntax for
comprehensions that would make this substantially clearer, more readable,
or more compact?
I'm guessing not :-)
(the compact bit comes from having to type the "for x in" part twice -- it
does *feel* a bit unnecessary. which is why I like numpy -- no "for" at all
:-)
(I'm still trying to figure out why folks think map() or filter() help
here...)
-CHB
--
Christopher Barker, Ph.D.
Oceanographer
Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
Chris.Barker at noaa.gov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180215/2df26da2/attachment-0001.html>
More information about the Python-ideas
mailing list