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] But I DO think my old the fly write up went wrong, converting do-notation to list comprehensions isn’t completely straightforward 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?) And yes the left identity law I posted was missing the [f(x)] brackets. If I’ve not made another mistake, that *should* now work? From: Chris Barker [mailto:chris.bar...@noaa.gov] Sent: 15 February 2018 23:34 To: jw14896.2...@my.bristol.ac.uk Cc: Evpok Padding <evpok.padd...@gmail.com>; Python-Ideas <python-ideas@python.org> Subject: Re: [Python-ideas] Temporary variables in comprehensions On Thu, Feb 15, 2018 at 2:13 AM, Jamie Willis <jw14896.2...@my.bristol.ac.uk <mailto:jw14896.2...@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.bar...@noaa.gov <mailto:chris.bar...@noaa.gov>
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/