檢視原始碼 推導式
在 Elixir 中,通常會對 Enumerable 進行迴圈,常會過濾一些結果並將值對映到另一個清單中。推導式是此類建構的語法糖:它們將這些常見任務分組到 for
特殊形式中。
例如,我們可以將整數清單對映到其平方值
iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
推導式由三部分組成:產生器、篩選器和可收集項。
產生器和篩選器
在上面的表達式中,n <- [1, 2, 3, 4]
是產生器。它實際上會產生要在推導式中使用的值。任何 Enumerable 都可以在產生器表達式的右側傳遞
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
。以下是進一步瞭解理解的其他資源