Skip to content

Commit 8634ef0

Browse files
author
José Valim
committed
Comprehensions chapter
1 parent 5c013c5 commit 8634ef0

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

‎getting_started/18.markdown‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,90 @@ guide: 18
55
---
66

77
# {{page.title }}
8+
9+
In Elixir, it is common to loop over Enumerables, often filtering some results, and mapping to another list of values. Comprehensions are syntax sugar for such constructs, grouping those common tasks into the `for` special form.
10+
11+
For example, we can get all the square values of elements in a list as follows:
12+
13+
```iex
14+
iex> for n <- [1, 2, 3, 4], do: n * n
15+
[1, 4, 9, 16]
16+
```
17+
18+
A comprehension is made of three parts: generators, filters and collectables.
19+
20+
## 18.1 Generators and filters
21+
22+
In the expression above, `n <- [1, 2, 3, 4]` is the generator. It is literally generating values to be used in the comprehensions. Any enumerable can be passed in the right-hand side the generator expression:
23+
24+
```iex
25+
iex> for n <- 1..4, do: n * n
26+
[1, 4, 9, 16]
27+
```
28+
29+
Generator expressions also support pattern matching, ignoring all non-matching patterns. Imagine that instead of a range, we have a keyword list where the key is the atom `:good` or `:bad` and we only want to calculate the square of the good values:
30+
31+
```iex
32+
iex> values = [good: 1, good: 2, bad: 3, good: 4]
33+
iex> for{:good, n} <- values, do: n * n
34+
[1, 4, 16]
35+
```
36+
37+
Alternatively, filters can be used to filter some particular elements out. For example, we can get the square of only odd numbers:
38+
39+
```iex
40+
iex> require Integer
41+
iex> for n <- 1..4, Integer.odd?(n), do: n * n
42+
[1, 9]
43+
```
44+
45+
A filter will keep all values except `nil` or `false`.
46+
47+
Comprehensions in general provide a much more concise representation than using the equivalent functions from the `Enum` and `Stream` modules. Furthermore, comprehensions also allow multiple generators and filters to be given. Here is an example that receives a list of directories and deletes all files in those directories:
48+
49+
```elixir
50+
for dir <- dirs,
51+
file <-File.ls!(dir),
52+
path =Path.join(dir, file),
53+
File.regular?(path) do
54+
File.rm!(path)
55+
end
56+
```
57+
58+
Keep in mind that variable assignments inside the comprehension, be it in generators, filters or inside the block, are not reflected outside of the comprehension.
59+
60+
## 18.2 Bitstring generators
61+
62+
Bitstring generators are also supported and are very useful when you need to organize bitstring streams. The example below receives a list of pixels from a binary with their respective red, green and blue values and convert them into triplets:
63+
64+
```iex
65+
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
66+
iex> for <<r::8, g::8, b::8 <- pixels>>, do:{r, g, b}
67+
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
68+
```
69+
70+
A bitstring generator can be mixed with the "regular" enumerable generators and provide filters as well.
71+
72+
## 18.3 Into
73+
74+
In the examples above, the comprehension was return a list as a result.
75+
76+
However, the result of a comprehension can be inserted into different data structures by passing the `:into` option. For example, we can use bitstring generators with the `:into` option to easily remove all spaces in a string:
77+
78+
```iex
79+
iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
80+
"helloworld"
81+
```
82+
83+
Sets, maps and other dictionaries can also be given with the `:into` option. In general, the `:into` accepts any structure as long as it implements the `Collectable` protocol.
84+
85+
For example, the `IO` module provides streams, that are both `Enumerable` and `Collectable`. You can easily implement an upcase echo terminal using comprehensions:
86+
87+
```iex
88+
iex> stream = IO.stream(:stdio, :line)
89+
iex> for line <- stream, into: stream do
90+
...> String.upcase(line) <> "\n"
91+
...> end
92+
```
93+
94+
Now type any string into the terminal and you will see the same value will be printed in upcase. Unfortunately, this example also got your shell stuck in the comprehension, so you will need to hit `Ctrl+C` twice to get out of it. :)

0 commit comments

Comments
(0)