当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Elixir Kernel.SpecialForms.for用法及代码示例


Elixir语言中 Kernel.SpecialForms.for 相关用法介绍如下。

用法:

for(args)
(宏)

理解允许您从可枚举或位串快速构建数据结构。

让我们从一个例子开始:

iex> for n <- [1, 2, 3, 4], do: n * 2
[2, 4, 6, 8]

推导式接受许多生成器和过滤器。可枚举生成器使用 <- 定义:

# A list generator:
iex> for n <- [1, 2, 3, 4], do: n * 2
[2, 4, 6, 8]

# A comprehension with two generators
iex> for x <- [1, 2], y <- [2, 3], do: x * y
[2, 3, 4, 6]

也可以给出过滤器:

# A comprehension with a generator and a filter
iex> for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
[2, 4, 6]

生成器也可用于过滤,因为它会删除与 <- 左侧的模式不匹配的任何值:

iex> users = [user: "john", admin: "meg", guest: "barbara"]
iex> for {type, name} when type != :guest <- users do
...>   String.upcase(name)
...> end
["JOHN", "MEG"]

位串生成器也受支持,并且在您需要组织位串流时非常有用:

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:uniq 选项

在上面的示例中,推导式返回的结果始终是一个列表。返回的结果可以通过传递 :into 选项来配置,只要它实现 Collectable 协议,它就可以接受任何结构。

例如,我们可以使用带有:into 选项的位串生成器来轻松删除字符串中的所有空格:

iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"

IO 模块提供流,它们都是 Enumerable Collectable ,这是一个使用理解的大写回显服务器:

for line <- IO.stream(), into: IO.stream() do
  String.upcase(line)
end

类似地,uniq: true 也可以用于理解,以保证只有在之前没有返回结果的情况下才会将结果添加到集合中。例如:

iex> for x <- [1, 1, 2, 3], uniq: true, do: x * 2
[2, 4, 6]

iex> for <<x <- "abcabc">>, uniq: true, into: "", do: <<x - 32>>
"ABC"

:reduce 选项

虽然:into 选项允许我们自定义对给定数据类型的理解行为,例如将所有值放入映射或二进制文件中,但这并不总是足够的。

例如,假设您有一个带有字母的二进制文件,您想计算每个小写字母出现的次数,忽略所有大写字母。例如,对于字符串 "AbCabCABc" ,我们要返回映射 %{"a" => 1, "b" => 2, "c" => 1}

如果我们要使用 :into ,我们需要一个数据类型来计算它所拥有的每个元素的频率。虽然 Elixir 中没有这样的数据类型,但您可以自己实现一个。

一个更简单的选择是对字母的映射和过滤使用理解,然后我们调用 Enum.reduce/3 来构建一个映射,例如:

iex> letters = for <<x <- "AbCabCABc">>, x in ?a..?z, do: <<x>>
iex> Enum.reduce(letters, %{}, fn x, acc -> Map.update(acc, x, 1, & &1 + 1) end)
%{"a" => 1, "b" => 2, "c" => 1}

虽然上面是straight-forward,但它的缺点是至少要遍历数据两次。如果您期望长字符串作为输入,这可能会非常昂贵。

幸运的是,推导式也支持 :reduce 选项,这将允许我们将上述两个步骤融合为一个步骤:

iex> for <<x <- "AbCabCABc">>, x in ?a..?z, reduce: %{} do
...>   acc -> Map.update(acc, <<x>>, 1, & &1 + 1)
...> end
%{"a" => 1, "b" => 2, "c" => 1}

当给定:reduce键时,其值用作初始累加器,必须将do块更改为使用->子句,其中->的左侧接收上一次迭代的累加值,右侧的表达式必须返回新的累加器值。一旦没有更多元素,则返回最终的累加值。如果根本没有元素,则返回初始累加器值。

相关用法


注:本文由纯净天空筛选整理自elixir-lang.org大神的英文原创作品 Kernel.SpecialForms.for(args)。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。