Enum.each与列表理解之间的区别。

时间:2017-11-24 11:14:06

标签: elixir

在Elixir中,我们可以使用Enum.each函数逐个打印列表的值。

a = [1, 2, 3]
Enum.each(a, fn(x) -> IO.puts x end)

输出是:

1
2
3
:ok

使用list comprehension执行相同的任务:

a = [1, 2, 3]
for x <- a, do: IO.puts x

输出是:

1
2
3
[:ok, :ok, :ok]

在输出结束时,Enum.each函数返回一个:ok,list comprehension返回三:ok。这两种方法有什么区别? 抱歉这个愚蠢的问题。我读了官方的灵药医生。但没有找到答案。

3 个答案:

答案 0 :(得分:5)

到目前为止提供的答案,虽然正确无法解决您问题的核心问题。这里你的问题的核心是你不熟悉Enum.eachEnum.mapfor x <- a等的实施方式。

引擎盖下的

Enum.map只是一个接受两个输入的递归函数:

  1. 一个清单
  2. 需要应用于该列表中每个元素的函数
  3. 此递归函数的返回值是一个新列表。

    进一步澄清: 假设您已将列表[1, 2, 3, 4]和函数fn e -> e * 2 end作为输入,那么您将获得新列表[2, 4, 6, 8]

    Enum.map可以像这样实现:

    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
    

    因此MyEnum.map([1,2,3], fn e -> e * 2 end)会向您返回列表:[2, 4, 6]

    现在假设你在这里使用函数IO.puts/2,如: MyEnum.map([1,2,3], fn e -> IO.puts(e) end),变量projected_value最终将是IO.puts/2的返回值。它将值打印到屏幕上,并返回:ok原子。

    这意味着在这种情况下构建的列表将在每个新递归上累积原子:ok

    引擎盖下的理解for x <- a类似于Enum.map,这是一个递归函数,它将提供的函数应用于列表的每个元素,但只是使用不同的语法。

    Enum.each也是一个递归函数,但它只会循环遍历每个元素,而不是关心在过程中积累任何东西。这是你如何实现它:

    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
    

    如您所见,此函数将始终返回:okEnum.map将始终返回新列表。

    所有这些都是基本的东西,所以:

    1. 如果你明白发生了什么事情,并且;
    2. 你明白基本上Enum.mapfor x <- [1,2,3]基本上是一个相同的递归函数,只有一个不同的接口
    3. |>然后应该澄清所有问题。

      此外,Javascript中也提供了相同的结构。如果您在JS中执行[1,2,3,4].map[1,2,3,4].each,那么它们与Elixir完全相同,因此如果您在Javascript中首先理解这些结构,您可以更清楚地了解这些结构理解差异仍然存在问题。

答案 1 :(得分:3)

Enum.each/2非常合适,如果你想在整个系列中做一些事情而不关心结果。 :ok总是由于此功能而返回。

List comprehension返回新的集合,这是对原始可能事物(在您的案例列表中)的每个元素的函数调用的计算结果。将其视为Enum.map/2的替代方案。

在您的情况下,结果为[:ok, :ok, :ok],因为:ok也是每次IO.puts来电的结果。

答案 2 :(得分:1)

简单地说,Enum.each/2 是迭代,而Enum.map/2 理解是 mappers ,理解可以在某种程度上一个reducer