For, Map and Reduce in Elixir

for,-map-and-reduce-in-elixir

Introduction

Run in Livebook

One of the students in my Introduction to Functional Programming course recently submitted a code snippet. It became evident that they assumed Elixir’s ‘for’ construct operates similarly to ‘for’ loops in non-functional programming languages. However, this is not the case, as Elixir’s ‘for’ is fundamentally different in its behavior.

What’s a list comprehension?

The command ‘for’ in Elixir is a list comprehension. The result of a ‘for’ is a list.

For instance, in the example below, ‘i’ goes from one to ten. The result is a list containing each value of ‘i’ multiplied by 10.

for i <- 1..10 do
  i * 10
end
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

You could do the same using Enum.map/2.

1..10
|> Enum.map(fn x -> x * 10 end)
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

If, instead, you wanted the result of the sum of all the values of the list, you would have two options.

The first one is to use Enum.sum() to sum all values of the resulting list.

1..10
|> Enum.map(fn x -> x * 10 end)
|> Enum.sum()
550

The second option is to use Enum.reduce/2:

1..10
|> Enum.reduce(fn x, accum -> x * 10 + accum end)
541

What if I wanted to multiply all values of the resulting list? The following solution would not work.

1..10
|> Enum.reduce(fn x, accum -> x * 10 * accum end)
3628800000000000

Because this is the value of 10 *… * 100:

10 * 20 * 30 * 40 * 50 * 60 * 70 * 80 * 90 * 100
36288000000000000

The correct way is:

1..10
|> Enum.reduce(1, fn x, accum -> x * 10 * accum end)
36288000000000000

What’s the difference between Enum.reduce/2 and Enum.reduce/3?

Back to for

‘For’ allows you to have more than one generator (the ‘i <- 1..10' part):

for i <- 1..3, j <- ["Brasil", "Mexico", "Angola"] do
  {:number, i, :country, j}
end
[
  {:number, 1, :country, "Brasil"},
  {:number, 1, :country, "Mexico"},
  {:number, 1, :country, "Angola"},
  {:number, 2, :country, "Brasil"},
  {:number, 2, :country, "Mexico"},
  {:number, 2, :country, "Angola"},
  {:number, 3, :country, "Brasil"},
  {:number, 3, :country, "Mexico"},
  {:number, 3, :country, "Angola"}
]

You can also add filters:

require Integer

for i <- 1..3,
    j <- ["Brasil", "Mexico", "Angola"],
    Integer.is_even(i),
    String.starts_with?(j, "B") do
  {:number, i, :country, j}
end
[{:number, 2, :country, "Brasil"}]

There are many more things that you can do with ‘for’, ‘map’ and ‘reduce’. Explore Elixir’s docs to learn more!

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
the-diffing-dilemma!-all-about-diffing-with-lazylists!

The Diffing Dilemma! All about diffing with LazyLists!

Next Post
resource-forecasting-guide-for-project-managers

Resource Forecasting Guide for Project Managers

Related Posts
c++-指向類別成員的指位器的實作細節

C++ 指向類別成員的指位器的實作細節

C++ 可以定義指向成員函式的指位器, 不過因為成員函式可能是虛擬函式, 如何能夠透過指向成員函式的指位器達到呼叫正確的成員函式呢?本來就來簡單探究。(本文均以 g++ 為例, 並且只探討單純的單一繼承)。 指向非虛擬函式的指位器 首先來看個簡單的範例, 建立指向非虛擬函式的指位器: #include using namespace std; class A { public:…
Read More