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/

Reply via email to