Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to do conditional list comprehension

I just started learning Haskell and I can't seem to find a good solution to create a list conditionally.

Basically, what I want is to do list comprehension with an if/else but without the else part. I am certain this is possible, I guess I am just using the wrong keywords on my googling quest.

A very stupid example in Python which I want to Haskellize:

[x for x in range(11) if x > 5]

In Haskell, as far as I understand we cannot omit else blocks like I did in the Python example. How should I do something like this? Does something like nothing exists which I can add to the else-block in list comprehension, like so:

[if x > 5 then x else Nothing | x <- [0..10]]

I actually came across Nothing in Haskell, though I haven't figure it out yet. It certainly doesn't seem to do what I hoped. Basically I don't want an else in my list comprehension, but if it's a necessary evil I want to insert nothing in the else block.

I can think of a bunch of hacks to get similar functionality very inefficiently:

  • filter the list after creating it, e.g. filter (>5) [0..10]
  • create a list of lists using list comprehension, for which I can create an empty list in the else block and then concat them, e.g. concat [if x > 5 then [x] else [] | x <- [0..10]]

These ideas all seem really ugly, though.

Ofcourse in practice I don't want to create conditional lists with such trivial conditions.

like image 503
Marc Claesen Avatar asked Apr 24 '14 13:04

Marc Claesen


People also ask

Is list comprehension faster than for loop?

List comprehensions are faster than for loops to create lists. But, this is because we are creating a list by appending new elements to it at each iteration.

What is list comprehension syntax?

A list comprehension is a syntactic construct available in some programming languages for creating a list based on existing lists. It follows the form of the mathematical set-builder notation (set comprehension) as distinct from the use of map and filter functions.

Where does the if statement go in list comprehension?

You can add an if statement at the end of a list comprehension to return only items which satisfy a certain condition. For example, the code below returns only the numbers in the list that are greater than two.


1 Answers

Use this:

Prelude> [x | x <- [0..10], x > 5]
[6,7,8,9,10]

In Haskell list comprehensions, the "source" expression and all the filters/ifs are "siblings", i.e. there's not much syntactic distinction between them, unlike in Python. So

<expr1> for <source_expr> if <cond_expr>

is just this in Haskell:

[<expr1> | <source_expr>, <cond_expr>, ...]

(source_expr being x in range(0, 10) in Python or x <- [0..9] in Haskell)

and you can have as many "source" and "filter" expressions in a Haskell list comprehension as you like.

This also means that you can write stuff in a style more similar to the mathematical notation; consider:

{ x : x ∈ [0, 10), x > 5 }

and see how the Haskell version is almost the same, compared to the Python one, which looks much more procedural/imperative.


This also works out trivially without any need for additional syntax/constructs with multiple "source" expressions:

Prelude> [(x, y) | x <- [0..10], y <- [10..20], y - x < 5]
[(6,10),(7,10),(7,11),(8,10),(8,11),(8,12),(9,10),(9,11),(9,12),(9,13),(10,10),(10,11),(10,12),(10,13),(10,14)]

In Python you would have to have what looks like a nested list comprehension, however Haskell still just sticks to the mathematical approach/notation.

like image 150
Erik Kaplun Avatar answered Sep 21 '22 02:09

Erik Kaplun