Reputation:
In Elixir, we can print the value of a list one by one by using Enum.each function.
a = [1, 2, 3]
Enum.each(a, fn(x) -> IO.puts x end)
the output is:
1
2
3
:ok
Using list comprehension to do the same task:
a = [1, 2, 3]
for x <- a, do: IO.puts x
the outputs is:
1
2
3
[:ok, :ok, :ok]
At the end of the output, Enum.each function returns one :ok, list comprehension returns three :ok. What is the difference between the two approach? Sorry for this dumb question. I read the official elixir doc. but didn't find the answer.
Upvotes: 11
Views: 7143
Reputation: 1970
The answers provided so far, although correct do not really address the core of your problem. The core of your problem here is that you are unfamiliar with how Enum.each
, Enum.map
, for x <- a
etc. are implemented under the hood.
Enum.map
under the hood is nothing but a recursive function that accepts two inputs:
The return value of this recursive function is a new list.
To clarify even more:
say you have as input the list [1, 2, 3, 4]
and a function: fn e -> e * 2 end
then you will get the new list [2, 4, 6, 8]
Enum.map
could simply be implemented like this:
defmodule MyEnum do
def map(list, fun) do
if Enum.empty?(list) do
[] # Null case reached, stop recursing.
else
[head | tail] = list # obtain the first element in the list, assign that value to `head`. Store the remaining list inside the variable `tail`
projected_value = fun.(head) # This is where your function gets applied
[ projected_value | map(tail, fun)] # Recurse
end
end
end
So MyEnum.map([1,2,3], fn e -> e * 2 end)
will return to you the list: [2, 4, 6]
Now suppose that you utilize the function IO.puts/2
here, like:
MyEnum.map([1,2,3], fn e -> IO.puts(e) end)
, the variable projected_value
will eventually be the return value of IO.puts/2
. It prints the value to the screen, and it returns an :ok
atom.
This means that the list you are building up in this case would be accumulating the atoms :ok
over each new recursion.
The comprehension for x <- a
under the hood is something similar to Enum.map, a recursive function that will apply a provided function to each element of your list, but just with a different syntax.
Enum.each
is also a recursive function under the hood, but it will just loop over every element, not caring about accumulating anything in the process. This is how you could implement it:
defmodule MyEnum do
def each(list, fun) do
if Enum.empty?(list) do
:ok # Return `:ok` to communicate the entire list has been traversed
else
[head | tail] = list
fun.(head) # Only apply the function to the element of the list, and do not care about its return value
each(tail, fun) # Continue to traverse the list recursively
end
end
end
As you can see, this function will always return :ok
and Enum.map
will always return a new list.
All of this is elementary stuff, so:
Enum.map
and for x <- [1,2,3]
are basically one and the same recursive function with just a different interface|>
then all your questions should be clarified.
In addition, the same constructs are provided in Javascript
as well. If you do [1,2,3,4].map
or [1,2,3,4].each
in JS, they do the exact same thing as in Elixir
, so you could find more clarity in understanding these constructs in Javascript
first if you still have a problem understanding the differences.
Upvotes: 12
Reputation: 121020
Simply put, Enum.each/2
is an iteration, while Enum.map/2
and the comprehension are both mappers, and the comprehension could be a reducer at some extent.
Upvotes: 1
Reputation: 5812
Enum.each/2 would be good fit, if you want to do something over the collection and you don't care about the result. :ok
is always return as a result of this function.
List comprehension returns you new collection, which is the result of computation of function call for each element of the original enumable thing (in your case list). Think about it as alternative for Enum.map/2
.
In your case result is [:ok, :ok, :ok]
, because :ok
is also the result for each IO.puts
call.
Upvotes: 3