【问题标题】:Why is my Elixir function returning a list of lists?为什么我的 Elixir 函数返回列表列表?
【发布时间】:2026-02-22 14:20:05
【问题描述】:

我有一个由函数 make_struct 返回的结构,就像这样:

%Example.Struct{example: 1, example_again: 2}

(即不带括号,例如:[ %Example... ]),至少我认为它会像上面一样返回,但该函数位于 GenServer 模块中,有时我注意到我的函数由于某种原因返回列表中的值,但我没有没看懂?

我想返回这些结构的列表:

def foo(data_as_a_list) do
   my_list = []
   for entry <- data_as_a_list do
     my_list = [ make_struct(entry) | my_list ]
   end
end

这将返回一个结构列表,每个结构都在一个列表中:

[
  [ 
    %Example.Struct{example: 1, example_again: 2} 
  ],
  [ 
    %Example.Struct{example: 1, example_again: 2} 
  ]
]

我只想:

[
   %Example.Struct{example: 1, example_again: 2},
   %Example.Struct{example: 1, example_again: 2}
]

【问题讨论】:

  • 注意如果Enum.map(data_as_list, fn entry -&gt; make_struct(entry) end),那么我得到了我想要的列表,但我仍然不明白为什么我上面的方法返回一个列表中每个结构的列表?

标签: elixir


【解决方案1】:

Elixir 的 for 不像大多数语言的 for 循环那样工作 - 它们是理解,而不是循环。我建议阅读the Elixir guide's page on it

Comprehensions 已经返回一个列表(默认情况下;当指定 :into 时可能是另一个 Collectable),其中包含 fordo 块的每次执行的返回。在简单的情况下,它就像Enum.map。因此,您可以使用其中任何一个来获得所需的结果:

def foo(data_as_a_list) do
  for entry <- data_as_a_list do
    make_struct(entry)
  end
end
def foo(data_as_a_list) do
  Enum.map(data_as_a_list, &make_struct/1)
end

我认为第二个更惯用,因为mapping 函数非常常见。理解主要用于当您有更复杂的需求时。

【讨论】:

    【解决方案2】:

    在您的代码中有几件事需要指出。

    1. 您所做的是分配my_list = [],然后使用for 更新my_list 的想法是错误的。

    这在语法上没有错误,这解释了为什么您没有收到错误消息,但是您将事情搞混了。

    Elixir 中的东西是不可变的。这意味着在您的for 中,您并没有真正更新my_list,而是在每个“迭代”上定义新的my_lists,其范围为for 构造,与它之外的内容无关。

    当您在iex 中运行类似a = 2 的内容时,您会返回2,因为“赋值”会返回分配的值。或者因为结果是计算的最后一件事,即2

    考虑到这一点,您的代码相当于:

    def foo(data_as_a_list) do
       my_list = []
       for entry <- data_as_a_list do
         [ make_struct(entry) | my_list ]
       end
    end
    

    但是请注意,由于事物是不可变的,因此每次读取 [ make_struct(entry) | my_list ] 时,my_list 等于 []。在更新的代码和原始代码中。

    1. 中的 for 不是迭代器构造,而是列表推导式构造。

    在 Elixir 中,for 不只是循环遍历事物,而是生成一个列表,其中包含对事物的操作结果。最简单的例子是:for i &lt;- 1..5, do: i 生成 [1, 2, 3, 4, 5],尽管事先没有正式的列表或更新 for 构造中的列表。

    考虑到这两件事,如果您想要在data_as_a_list 的每个项目上获得一个调用make_struct 的列表,那么使用for 实现它的方法是:

    def foo(data_as_a_list) do
       for entry <- data_as_a_list do
         make_struct(entry)
       end
    end
    

    【讨论】: