I've heard from many Pythonists that they prefer list comprehensions because they can do everything you can do using high order functions such as filter and reduce, and more. So this question address them: what is a solid example of something you can do with them, that is tricky to do with HOFs?
List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list. Example: Based on a list of fruits, you want a new list, containing only the fruits with the letter "a" in the name.
List comprehension is an elegant way to define and create lists based on existing lists. List comprehension is generally more compact and faster than normal functions and loops for creating list. However, we should avoid writing very long list comprehensions in one line to ensure that code is user-friendly.
One of the language's most distinctive features is the list comprehension, which you can use to create powerful functionality within a single line of code.
Conclusions. List comprehensions are often not only more readable but also faster than using “for loops.” They can simplify your code, but if you put too much logic inside, they will instead become harder to read and understand.
The answer is that there is no such example. Everything you can do with list comprehensions has a mechanical translation to higher-order functions. In fact, this is how Haskell implements list comprehensions: it desugars them to higher-order functions.
Given a list comprehension like this:
[(x, y) | x <- [1..3], y <- [4..6]]
Haskell desugars it to:
concatMap (\x -> concatMap (\y -> [(x, y)]) [4..6]) [1..3]
Similarly, if you put in predicates like:
[(x, y) | x <- [1..3], y <- [4..6], x + y /= 5]
... then that desugars to:
concatMap (\x -> concatMap (\y -> if (x + y) == 5 then [(x, y)] else []) [4..6]) [1..3]
In fact, this desugaring is part of the Haskell specification, which you can find here.
As has been said, everything you can do with list comprehensions can be desugared into higher-order functions, but a large part of the problem with doing this in Python is that Python lacks support for the kind of point-free programming you can use with filter
, map
, and friends in Haskell. Here's a somewhat contrived example, but I think you'll get the idea.
Let's take this Python code:
[(x,y) for x,y in zip(xrange(20), xrange(20, 0, -1)) if x % 2 == 0 and y % 2 == 0]
All it does is print this out:
[(0, 20), (2, 18), (4, 16), (6, 14), (8, 12), (10, 10), (12, 8), (14, 6), (16, 4), (18, 2)]
Here's the equivalent version with filter:
filter(lambda ns : ns[0] % 2 == 0 and ns[1] % 2 == 0, zip(xrange(20), xrange(20, 0, -1)))
I hope you'll agree with me that it's a lot uglier. There isn't really much you can do to make it less ugly without defining a separate function.
But let's look at the equivalent version in Haskell:
[(x,y) | (x,y) <- zip [0..20] [20,19..0], x `mod` 2 == 0 && y `mod` 2 == 0]
Okay, pretty much as good as the Python list comprehension version. What about the equivalent filter version?
import Data.Function
let f = (&&) `on` (==0) . (`mod` 2)
filter (uncurry f) $ zip [0..20] [20,19..0]
Okay, we had to do an import, but the code is (imo) a lot clearer once you understand what it does, although some people might still prefer f
to be pointed, or even a lambda with filter. In my opinion the point-free version is more concise and conceptually clear. But the main point I want to make is that it is not really going to be this clear in Python because of the inability to partially apply functions without bringing in a separate library, and the lack of a composition operator, so in Python it is a good idea to prefer list comprehensions over map/filter, but in Haskell it can go either way depending on the specific problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With