この記事は、Elixir Advent Calendar 2024 シリーズ12 の24日目です
【本コラムは、15分で読め、5分で試せます】
piacere です、ご覧いただいてありがとございます
@RyoWakabayashi さんの 配列から全順列の組み合わせを拾うコラム で for
による多重リスト処理と再帰の組み合わせテクニックがあったので解説します
基本
下記は、[1, 3]
の要素を1つずつ出し、そこに [2, 4]
の要素を1つずつ乗算するという、for
による2重リスト処理の例です
iex> for x <- [1, 2], y <- [3, 4], do: x * y
[3, 4, 6, 8]
2重だけで無い多重リストも指定可能です
iex> for x <- [1, 2], y <- [3, 4], z <- [5, 6], do: x * y * z
[15, 18, 20, 24, 30, 36, 40, 48]
変動するリストの指定
固定リストだけで無く、動的に変動するリストの指定も可能です
iex> for x <- Enum.shuffle([1, 2]), y <- Enum.shuffle([3, 4]), do: x * y
[8, 6, 3, 4]
iex> for x <- Enum.shuffle([1, 2]), y <- Enum.shuffle([3, 4]), do: x * y
[6, 8, 4, 3]
iex> for x <- Enum.shuffle([1, 2]), y <- Enum.shuffle([3, 4]), do: x * y
[6, 8, 3, 4]
要素同士が重複しない組み合わせを作る
for
の2重リスト目を、y <- list -- [x]
と1重リスト目の要素を除外するように作ると、1重リスト目と2重リスト目で同じ要素が出ない組み合わせを生成できます
iex> list = [1, 2, 3]
iex> for x <- list, y <- Enum.map(list -- [x], & [&1]), do: [x | y]
[[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
このように、各リストが影響し合う多重リスト処理も作れます
配列から全順列の組み合わせを拾うコラム
上記までの知識があれば、下記 @RyoWakabayashi さんの 配列から全順列の組み合わせを拾うコラム も読みこなせるようになれます
こちらのコードは、再帰の中で、再帰を繰り返し元として呼ぶ for
が、下記「+」にあります
defmodule Permutations do
…
def all(_, 0), do: [[]]
def all([], _), do: []
def all(list, n) do
for x <- list,
rest = list -- [x],
+ perm <- all(rest, n - 1),
do: [x | perm]
end
end
これを上記例と同じ書き方に改めると、こうなります
なお、削除した rest = list -- [x]
のように、中間変数を定義し、多重リスト処理に利用できることも for
の高度な仕様です
defmodule Permutations do
…
def all(_, 0), do: [[]]
def all([], _), do: []
def all(list, n) do
for x <- list,
+ perm <- all(list -- [x], n - 1),
do: [x | perm]
end
end
もっと同じような記述に改めると、より読みこなしやすくなるでしょう
defmodule Permutations do
…
def all(_, 0), do: [[]]
def all([], _), do: []
def all(list, n) do
+ for x <- list, y <- all(list -- [x], n - 1), do: [x | y]
end
end