To help myself in learning more Elixir, I'm going to continue these Elixir guide/tutorial posts where I try to explain a different piece of Elixir in each post. Today's post will be covering the Enum module and it's usage.
The Enum Module provides a set of algorithms that enumerate over collections according to the Enumerable protocol:
iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
[2,4,6]
Some particular types, like dictionaries, yield a specific format on enumeration. For dicts, the argument is always a {key, value} tuple:
iex> dict = %{a: 1, b: 2}
iex> Enum.map(dict, fn {k, v} -> {k, v * 2} end)
[a: 2, b: 4]
Below are some other functions available in the Enum Module along with a link to it's definition in the Enum Module documentation.
Let’s first talk about capturing function. Capture means "&" can turn a function into an anonymous function which can be passed as arguments to other function or be bound to a variable.
& can capture two types of functions, a function with given name and arity from a module.
The notation is: &(module_name.function_name/arity)
ex:
speak = &(IO.puts/1)
speak.("hello") # hello
We capture puts function from IO module and bind it with a local name speak.
The capture operator can be a little difficult to wrap your head around, so here are some examples and a helpful image to help grasp this concept:
# Multiple each number by itself
Enum.map [1, 2, 3], fn(num) ->
num * num
end
# Shortened with capture operator:
# Parentheses are required around the capture in this
# case to make it clear where the capture starts and ends.
Enum.map([1, 2, 3], &(&1 * &1))
When you are capturing a named function, you don’t need the parentheses:
# Remove \n chars from the end of each word
Enum.map ["hello\n", "there\n"], fn(word) ->
String.replace(word, "\n", "")
end
# Shortened with capture operator:
Enum.map(["hello\n", "there\n"],
&String.replace(&1, "\n", ""))
Read the documentation on the Capture operator for more details.
Stream is a lazy version of the Enumerable module. Note that the functions in the Enum module are eager: they always start the enumeration of the given collection. The Stream module allows lazy enumeration of collections and provides infinite streams. It implements most Enum functions, but instead of returning a modified list, it returns a struct like this:
%Stream{
enum: [...], # Enumerable to iterate through
funs: [...] # Anonymous functions to run
}
Since the majority of the functions in Enum enumerate the whole collection and return a list as result, infinite streams need to be carefully used with such functions, as they can potentially run forever. For example:
Enum.each Stream.cycle([1,2,3]), &IO.puts(&1)
Streams are lazy, and only iterate over the list once:
# Iterates over the list twice
list
|> Enum.filter(&is_number/1)
|> Enum.filter(&(&1 * 2 == 4))
# Iterates over the list once
list
|> Stream.filter(&is_number/1)
|> Stream.filter(&(&1 * 2 == 4))
|> Enum.into([])
Use Enum.into/2 or Stream.run/1 to make a stream do work.
list
|> Stream.filter(&is_number/1)
|> Stream.filter(&(&1 * 2 == 4))
|> Enum.into([])
[1, 2, 3]
|> Stream.each(&IO.puts/1)
|> Stream.run
Checkout some more Stream building functions in the documentation: - cycle/1 - iterate/2 - resource/3
tags: functions enum elixir code programming