解析

列表解析 (List comprehensions) 是在 Elixir 中通過列舉來循環 (looping) 的語法糖 (syntactic sugar)。在本課程中,我們將看看如何使用解析 (comprehensions) 來進行疊代 (iteration) 和生成 (generation)。

Table of Contents

基礎

在很多時候,解析可以用來為 EnumStream 疊代產生更簡潔的語句。讓我們先看一個簡單的解析,然後再拆解它的結構:

iex> list = [1, 2, 3, 4, 5]
iex> for x <- list, do: x*x
[1, 4, 9, 16, 25]

我們注意到的第一件事是使用 for 和生成器 (generator)。生成器是什麼?生成器是在列表解析中找到的 x <- [1, 2, 3, 4] 表達式,它負責產生下一個值。

對我們來說幸運的是,解析不僅限於列表,實際上它能在任何列舉上使用:

# Keyword Lists
iex> for {_key, val} <- [one: 1, two: 2, three: 3], do: val
[1, 2, 3]

# Maps
iex> for {k, v} <- %{"a" => "A", "b" => "B"}, do: {k, v}
[{"a", "A"}, {"b", "B"}]

# Binaries
iex> for <<c <- "hello">>, do: <<c>>
["h", "e", "l", "l", "o"]

如同 Elixir 中的許多其他東西,生成器依靠模式比對將其輸入集 (input set) 與左側變數進行比較。在無法找到配對的情況下,該值將被忽略:

iex> for {:ok, val} <- [ok: "Hello", error: "Unknown", ok: "World"], do: val
["Hello", "World"]

可以同時使用多個生成器,就像巢狀迴圈一樣 (nested loops):

iex> list = [1, 2, 3, 4]
iex> for n <- list, times <- 1..n do
...>   String.duplicate("*", times)
...> end
["*", "*", "**", "*", "**", "***", "*", "**", "***", "****"]

為了更好地說明正在執行的循環,我們使用 IO.puts 來顯示兩個被生成的值:

iex> for n <- list, times <- 1..n, do: IO.puts "#{n} - #{times}"
1 - 1
2 - 1
2 - 2
3 - 1
3 - 2
3 - 3
4 - 1
4 - 2
4 - 3
4 - 4

列表解析是語法糖,只有在適當的時候才應該使用。

篩選器 (Filters)

可以把篩選器看作是解析式的監視 (guard)。當篩選值回傳 falsenil 時,它將被排除在最終列表之外。讓我們在一個範圍內循環,並只注意偶數。我們將使用 Integer 模組中的 is_even/1 函數來檢查一個值是否是偶數。

import Integer
iex> for x <- 1..10, is_even(x), do: x
[2, 4, 6, 8, 10]

如同生成器 (generators),可以同時使用多個篩選器。現在擴展範圍,然後僅對偶數且可被 3 整除的值進行篩選。

import Integer
iex> for x <- 1..100,
...>   is_even(x),
...>   rem(x, 3) == 0, do: x
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]

使用 :into

如果想產生一個列表 (list) 以外的東西呢?加入 :into 選項,就可以做到這一點!經驗上來說, :into 接受任何能實現 Collectable 協定的結構。

要使用 :into,讓我們從關鍵字列表中建立一個映射:

iex> for {k, v} <- [one: 1, two: 2, three: 3], into: %{}, do: {k, v}
%{one: 1, three: 3, two: 2}

由於二進位 (binaries) 也是可群集 (collectables) 的,所以可以使用列表解析和 :into 來建立字串:

iex> for c <- [72, 101, 108, 108, 111], into: "", do: <<c>>
"Hello"

就這樣!列表解析是能以簡潔方式疊代群集的簡單方法。

Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!