1.OverView
さーて、Elixir Schoolで上がってる各関数のチートシートをどんどん作りますかー。
個人的に、こんなもん覚えなくていいよ、くらいの勢いなので、Day5を理解出来たなら、流し読み流し読み。
僕も、手を動かした以上の意味を感じてないです。
2. それでは解説
2.1 all?とany?
コレクションの要素を一つずつ「評価」し、all?の場合はすべての要素が、any?の場合はどれかの要素がtrueなら、OKとする。
all?
iex(120)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex(123)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 4 end)
false
any?
iex(124)> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 4 end)
true
上のlistの文字列の長さが、3,3,5ですので、4以上なのは最後のhelloだけですね。
なので、上記の匿名関数fn(s) -> String.length(s)の結果は、
3 -> false
3 -> false
5 -> true
なので、すべてが
all?なら、false, any?ならtrueとなってます。
2.2 chunk_every
これは、無名関数は引数に取らないですなぁ。
要素を、引数2で指定した数で、分割してくれます。
iex(4)> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]
iex(5)> Enum.chunk_every([1, 2, 3, 4, 5, 6], 3)
[[1, 2, 3], [4, 5, 6]]
2.3 chunk_by
こちらは、Chunnkを自分で指定した関数で処理してくれますね。
よくわからんので、helpを見てみましょう。
… def chunk_by(enumerable, fun)
@spec chunk_by(t(), (element() -> any())) :: [list()]
Splits enumerable on every element for which fun returns a new value.
自分の関数の戻り値で、分割してくれるみたいです。
ちょっと、Elixir Schoolの例が面白いので、実行してみましょう
iex(9)> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]
iex(10)>
fn(x) -> String.length(x) endは、文字列の長さ(length)を返します。
対象のlistに、"nop"`を追加してみましょう。
これ、3文字だから、どのグループに追加されるかな?
iex(131)> Enum.chunk_by(["one", "two", "three", "four", "five", "six","nop"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six", "nop"]]
見事、sixと同じlistに追加されました。
ただの戻り値の分類であれば、
[["one", "two","six", "nop"], ["three"], ["four", "five"]
になるべきだと思うので。これ、課題に入れときますね。
2.4 map_every
このmap_everyは、すべての要素を、二つ目の引数で指定した数毎(nth)に関数(fun)で評価します。
iex(19)> h Enum.map_every
def map_every(enumerable, nth, fun)
@spec map_every(t(), non_neg_integer(), (element() -> any())) :: list()
since: 1.4.0
Returns a list of results of invoking fun on every nth element of enumerable,
starting with the first element.
関数を最初に適用してから3つ毎に同様の処理
iex(20)> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]
3個おきに処理してますなー。
2.5 ちょっとお知らせ
さて、ここで以下の関数は、明日に回して、サクッとほかの関数を終わらせちゃいますね。
each
map
filter
reduce
2.6 min/max
min コレクションの最小値を返す
max コレクションの最大値を返す
これは説明不要?
iex(21)> Enum.min([1, 2, 3, 4, 5, 6, 7, 8])
1
iex(22)> Enum.max([1, 2, 3, 4, 5, 6, 7, 8])
8
listの中の最大値(max)、最小値(min)を返します。
面白いのはmin/2とmax/2の存在
コレクションが空であった場合にあらかじめ最小値を生成する為の関数を渡すことができます。
iex(23)> Enum.min([], fn -> :foo end)
:foo
2.7 sort
はい、ソートです。
iex(25)> Enum.sort([8, 2, 3, 4, 5, 6, 7, 1])
[1, 2, 3, 4, 5, 6, 7, 8]
順番に並んでますね。
2.7 sort_by
sort_byは、sortする関数を指定出来ます。
iex(138)> numlist = ["one", "two", "three", "four", "five", "six","nop"]
["one", "two", "three", "four", "five", "six", "nop"]
iex(139)> Enum.sort_by(numlist, fn(x) -> String.length(x) end)
["one", "two", "six", "nop", "four", "five", "three"]
こいつを使うと、先ほどのchunk_byの課題が解決しますねぇ。
iex(36)> Enum.sort_by(["one", "two", "three", "four", "five", "six","nop"], fn(x) -> String.length(x) end)
|> Enum.chunk_by( fn(x) -> String.length(x) end)
[["one", "two", "six", "nop"], ["four", "five"], ["three"]]
2.8 uniq
uniqの方は、いたって簡単。引数で引き渡されたlistから、重複しているものを削除してくれます。
iex(38)> Enum.uniq([1, 1, 2, 2, 3, 4, 5, 6, 7, 8])
[1, 2, 3, 4, 5, 6, 7, 8]
2.9 uniq_by
uniq_byはuniqする関数を指定できます。
これ、Elixir Schoolのサンプルがとても面白いので、くどく解説
iex(46)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end)
[%{y: 1, x: 1}, %{y: 3, x: 3}]
最初の引数、これ、mapを含んだlistですね。
[%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}]
このlistのそれぞれの要素がfn coord -> coord.y endに渡されます。
coord.y
これ、mapのキーが、atomだった時だけ、出来る要素の取り出し方ですね。
iex(59)> %{x: 2, y: 1}.y
1
iex(60)> %{x: 1, y: 1}.y
1
iex(61)> %{x: 3, y: 3}.y
3
つまり、最初の要素と、次の要素が、yが1なので、uniq_byの対象となる様です。
xの値を変えてみましょう。
iex(67)> Enum.uniq_by([%{x: 7, y: 1}, %{x: 4, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end)
[%{y: 1, x: 7}, %{y: 3, x: 3}]
先頭にある%{x: 7, y: 1}が処理された後、次の%{x: 4, y: 1}が来た時、重複の為、弾かれる様です。
なるほどねぇ。
3. 本日のチートシート!
Enumライブラリ(暫定)
関数名 | 説明 | 例 |
---|---|---|
all? | コレクションの要素を一つずつ「評価」し、すべての要素がtrueなら、OKとする。 | iex(120)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end) false iex(123)> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 4 end) false |
any? | コレクションの要素を一つずつ「評価」し、いずれかの要素がtrueなら、OKとする。 | iex(124)> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 4 end) true |
chunk_every | 要素を、引数2で指定した数で、分割します | iex(4)> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2) [[1, 2], [3, 4], [5, 6]] iex(5)> Enum.chunk_every([1, 2, 3, 4, 5, 6], 3) [[1, 2, 3], [4, 5, 6]] |
chunk_by | 要素を、指定した関数で処理して分割する | iex(9)> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end) [["one", "two"], ["three"], ["four", "five"], ["six"]] |
map_every | すべての要素を、二つ目の引数で指定した数毎(nth)に関数(fun)で評価します | 関数を最初に適用してから3つ毎に同様の処理 iex(20)> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end) [1001, 2, 3, 1004, 5, 6, 1007, 8] |
each | 次回 | |
map | 次回 | |
filter | 次回 | |
reduce | 次回 | |
min | コレクションの最小値を返す | iex(21)> Enum.min([1, 2, 3, 4, 5, 6, 7, 8]) 1 |
max | コレクションの最大値を返す | iex(22)> Enum.max([1, 2, 3, 4, 5, 6, 7, 8]) 8 |
min/2,max/2 | コレクションが空であった場合にあらかじめ最小値を生成する為の関数を渡すことができます。 | iex(23)> Enum.min([], fn -> :foo end) :foo |
sort | コレクションをソートする | Enum.sort([8, 2, 3, 4, 5, 6, 7, 1]) [1, 2, 3, 4, 5, 6, 7, 8] |
sort_by | 指定した関数で、sortを行う | iex(138)> numlist = ["one", "two", "three", "four", "five", "six","nop"] ["one", "two", "three", "four", "five", "six", "nop"] |
uniq | 引き渡されたコレクションから、重複しているものを削除する | iex(38)> Enum.uniq([1, 1, 2, 2, 3, 4, 5, 6, 7, 8]) [1, 2, 3, 4, 5, 6, 7, 8] |
uniq_by | 指定した関数で、重複を削除する | iex(46)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end) [%{y: 1, x: 1}, %{y: 3, x: 3}] |