On Tue, Aug 15, 2017 at 1:54 AM, Ian Kelly <ian.g.ke...@gmail.com> wrote: > On Mon, Aug 14, 2017 at 9:40 AM, Chris Angelico <ros...@gmail.com> wrote: >> On Tue, Aug 15, 2017 at 1:33 AM, Ian Kelly <ian.g.ke...@gmail.com> wrote: >>> On Sun, Aug 13, 2017 at 8:36 AM, Steve D'Aprano >>>> Sure. In Haskell, comprehensions are *implicit* loops, rather than >>>> explicit like >>>> in Python. >>> >>> No, they aren't. Haskell list comprehensions use lazy evaluation. >>> Here's an example of an infinite list comprehension: >>> >>> Prelude> let squares = [ x ** 2 | x <- [1 ..] ] :: [Float] >>> Prelude> :print squares >>> squares = (_t1::[Float]) >>> >>> You might say that this is more like a generator expression but the >>> result is in fact a list. We can evaluate the first four elements: >>> >>> Prelude> print $ take 4 squares >>> [1.0,4.0,9.0,16.0] >>> >>> And then see that these have been lazily evaluated in the list: >>> >>> Prelude> :print squares >>> squares = 1.0 : 4.0 : 9.0 : 16.0 : (_t2::[Float]) >>> >>> A Haskell list comprehension is not a loop at all. >> >> What if you don't take the first four, but instead take just the tenth >> element? Will the preceding elements be calculated too, or do you have >> a sparse list? If the latter, it's not really comparable to a Python >> list, but to some sort of cached mapping from input values to output >> values, which in Python I would implement as a dict with a __missing__ >> method. And if the former, well, that's still going to have a loop, >> and it's definitely like a genexp, but genexps have more functionality >> than they do in Python. > > The answer is, it depends. If it can avoid evaluating the earlier > elements it will: > > Prelude> squares !! 10 > 121.0 > Prelude> :print squares > squares = 1.0 : 4.0 : 9.0 : 16.0 : (_t3::Float) : (_t4::Float) : > (_t5::Float) : (_t6::Float) : (_t7::Float) : (_t8::Float) : 121.0 : > (_t9::[Float]) > > But sometimes it can't: > > Prelude> let triangle = [ (i,j) | i <- [1..], j <- [1..i*i] ] > Prelude> triangle !! 10 > (3,6) > Prelude> :print triangle > triangle = (1,1) : (2,1) : (2,2) : (2,3) : (2,4) : (3,1) : (3,2) : > (3,3) : (3,4) : (3,5) : (3,6) : (_t10::[(Integer, Integer)])
Well, no, it doesn't depend. It evaluates only those elements that get requested. You just requested all the earlier elements as part of calculating a triangle. :) So it's really nothing to do with a Python list, nor a list comprehension. It's like this lazy map: class Map(dict): def __init__(self, func, *coll): self.func = func self.coll = coll # Collections, not iterables def __missing__(self, key): self[key] = self.func(*(coll[key] for coll in self.coll)) return self[key] >>> squares = Map(lambda x: x*x, range(10000)) >>> squares[5] 25 >>> squares[1000] 1000000 >>> squares {5: 25, 1000: 1000000} Except that the indices used are restricted, presumably. to counting numbers. But still, what you call a lazy list is a function (in the mathematical sense), but not a true collection. ChrisA -- https://mail.python.org/mailman/listinfo/python-list