查看源代码 列表推导
在 Elixir 中,通常需要遍历一个可枚举对象,经常需要过滤掉一些结果并将值映射到另一个列表。列表推导是这种结构的语法糖:它们将这些常见任务分组到 for
特殊形式中。
例如,我们可以将一个整数列表映射到它们的平方值
iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
列表推导由三个部分组成:生成器、过滤器和收集器。
生成器和过滤器
在上面的表达式中,n <- [1, 2, 3, 4]
是**生成器**。它实际上是生成要用于列表推导的值。任何可枚举对象都可以传递到生成器表达式的右侧
iex> for n <- 1..4, do: n * n
[1, 4, 9, 16]
生成器表达式也支持对其左侧进行模式匹配;所有不匹配的模式都将被忽略。想象一下,不是范围,而是键为原子 :good
或 :bad
的关键字列表,我们只希望计算 :good
值的平方
iex> values = [good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- values, do: n * n
[1, 4, 16]
除了模式匹配之外,还可以使用过滤器来选择一些特定元素。例如,我们可以选择 3 的倍数,并丢弃所有其他元素
iex> for n <- 0..5, rem(n, 3) == 0, do: n * n
[0, 9]
列表推导会丢弃所有过滤器表达式返回 false
或 nil
的元素;所有其他值都会被选中。
列表推导通常比使用来自 Enum
和 Stream
模块的等效函数提供更简洁的表示。此外,列表推导还允许给出多个生成器和过滤器。这是一个接收目录列表并获取这些目录中每个文件大小的示例
dirs = ["/home/mikey", "/home/james"]
for dir <- dirs,
file <- File.ls!(dir),
path = Path.join(dir, file),
File.regular?(path) do
File.stat!(path).size
end
也可以使用多个生成器来计算两个列表的笛卡尔积
iex> for i <- [:a, :b, :c], j <- [1, 2], do: {i, j}
[a: 1, a: 2, b: 1, b: 2, c: 1, c: 2]
最后,请记住,列表推导内的变量赋值,无论是在生成器、过滤器还是代码块内,都不会反映在列表推导之外。
比特串生成器
比特串生成器也受支持,当您需要在比特串流上进行推导时非常有用。下面的示例接收来自二进制文件的像素列表,以及它们各自的红色、绿色和蓝色值,并将它们转换为包含三个元素的元组
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
比特串生成器可以与“常规”可枚举生成器混合使用,并且也支持过滤器。
:into
选项
在上面的示例中,所有列表推导都返回列表作为结果。但是,可以通过将 :into
选项传递给列表推导,将列表推导的结果插入到不同的数据结构中。
例如,比特串生成器可以与 :into
选项一起使用,以便轻松地从字符串中删除所有空格
iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"
集合、映射和其他字典也可以传递给 :into
选项。通常,:into
接受任何实现 Collectable
协议的结构。
:into
的一个常见用例是转换映射中的值
iex> for {key, val} <- %{"a" => 1, "b" => 2}, into: %{}, do: {key, val * val}
%{"a" => 1, "b" => 4}
让我们再举一个使用流的例子。由于 IO
模块提供了流(既是 Enumerable
又是 Collectable
),因此可以使用列表推导来实现一个回显终端,该终端会回显输入的字符串的大写版本
iex> stream = IO.stream(:stdio, :line)
iex> for line <- stream, into: stream do
...> String.upcase(line) <> "\n"
...> end
现在在终端中输入任何字符串,您将看到相同的字符串将以大写形式打印出来。不幸的是,这个例子也让您的 IEx shell 卡在了列表推导中,所以您需要按两次 Ctrl+C
才能退出它。:)
其他选项
列表推导支持其他选项,例如 :reduce
和 :uniq
。以下是一些关于列表推导的更多资源