概要
再帰使った方が関数型言語らしくみえるし、for
よりEnum
の方が最近の言語らしい。
ループをきれいに書くには?
list の要素を表示
まずは単純なもの。Enum
が短い。
list
IO.puts "for"
for x <- [1, 2, 3] do
IO.puts x
end
IO.puts "Enum"
Enum.map([1, 2, 3], &IO.puts(&1))
IO.puts "recursion"
defmodule Sample do
def show([x|tl]) do
IO.puts x
show(tl)
end
def show([]) do
# noop
end
end
Sample.show [1, 2, 3]
二重ループ
2つのリストの全組み合わせを表示するループ。
for
が短い。
再帰が汚い。。。
double_loop
IO.puts "for"
for x <- [1, 2, 3],
y <- [4, 5, 6] do
IO.inspect {x , y}
end
IO.puts "Enum"
Enum.map([1, 2, 3],
fn x ->
Enum.map([4, 5, 6], &IO.inspect {x, &1})
end)
IO.puts "recursion"
defmodule Sample do
def show([x|tl], y) do
show(x, y)
show(tl, y)
end
def show([], _) do
# noop
end
def show(x, [y|tl]) do
IO.inspect {x, y}
show(x, tl)
end
def show(_, []) do
# noop
end
end
Sample.show [1, 2, 3], [4, 5, 6]
条件付き2重ループ
x < y
となる組み合わせを表示するループ。
for
は filter を使いました。
再帰はあえて guard を使ってみました。
Enum
は汚く。。。
double_loop_and_conditional
IO.puts "for"
for x <- 1..3,
y <- 1..3,
x < y do
IO.inspect {x , y}
end
IO.puts "Enum"
Enum.map(1..3,
fn x ->
Enum.map(1..3,
fn y ->
if x < y do
IO.inspect {x, y}
end
end)
end)
IO.puts "recursion"
defmodule Sample do
def show([x|tl], y) do
show(x, y)
show(tl, y)
end
def show([], _) do
# noop
end
def show(x, [y|tl]) when x < y do
IO.inspect {x, y}
show(x, tl)
end
def show(x, [y|tl]) do
show(x, tl)
end
def show(_, []) do
# noop
end
end
Sample.show Enum.to_list(1..3), Enum.to_list(1..3)
最後に
for
最強!
ではなく、あきらかに経験不足だと思います。
パイプとか高階関数とか使うと綺麗に書けるのかもしれません。
Enum
的なもので次のように書けるものを作るといいのかもしれません。
ExEnum
ExEnum.map([1,2,3], [1,2,3], &IO.inspect({&1, &2})
ExEnum.map([[1,2,3], [1,2,3]], &IO.inspect({&1, &2})
また、当然状況によって使い分ける必要もあると思います。
経験積んで良い書き方が分かってきたら更新します。
追記
@antimon2 さんに 順列組合せの関数を使った回答例 を書いていただけました。
順列組合せの関数があればさくっと書けるレベルだと思いますが、順列組合せの関数から作っていただいているので、すごく勉強になります。
最後のは組合せを狙ったわけではなかったのですが、条件を見て簡潔な関数を選ぶところも参考になりました。