Elixir 发生器和筛选器

2023-12-16 20:46 更新

在上述表达式中,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> multiple_of_3? = fn(n) -> rem(n, 3) == 0 end
iex> for n <- 0..5, multiple_of_3?.(n), do: n * n
[0, 9]

推导式将所有筛选器表达式结果为falsenil的元素过滤掉了;其它所有值都被选入.

推导式通常提供比使用EnumStream模块中的相等函数更加简洁的表达方法.而且,推导式也允许指定多重生成器和筛选器.这里有一个例子,接收一个目录组成的列表,然后得到这些目录中每个文件的大小:

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]

一个更好的关于多重发生器和筛选器的案例是勾股数.勾股数是指满足a*a + b*b = c*c的正整数,让我们在文件triple.exs中编写一个推导式:

defmodule Triple do
  def pythagorean(n) when n > 0 do
    for a <- 1..n,
        b <- 1..n,
        c <- 1..n,
        a + b + c <= n,
        a*a + b*b == c*c,
        do: {a, b, c}
  end
end

然后在终端中运行:

iex triple.exs
iex> Triple.pythagorean(5)
[]
iex> Triple.pythagorean(12)
[{3, 4, 5}, {4, 3, 5}]
iex> Triple.pythagorean(48)
[{3, 4, 5}, {4, 3, 5}, {5, 12, 13}, {6, 8, 10}, {8, 6, 10}, {8, 15, 17},
 {9, 12, 15}, {12, 5, 13}, {12, 9, 15}, {12, 16, 20}, {15, 8, 17}, {16, 12, 20}]

当搜索范围是一个巨大的数时,上述代码会非常昂贵.此外,由于{b, a, c}{a, b, c}是相同的勾股数,所以我们的函数生成重复结果.我们可以优化推导式来消除重复的结果,即在后面引用先前发生器中的变量,例如:

defmodule Triple do
  def pythagorean(n) when n > 0 do
    for a <- 1..n-2,
        b <- a+1..n-1,
        c <- b+1..n,
        a + b + c <= n,
        a*a + b*b == c*c,
        do: {a, b, c}
  end
end

最后,记住推导式内部的变量赋值,即发生器,筛选器或块内的,不会影响到推导式的外部.

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号