ここまでのあらまし
前回のドキュメントElixir~名前付き関数とモジュール化~ でEnum.mapで各要素をIO.putsで出力することをしました。
今回は、Enum.mapにEnum.mapを入れ子にするとどうなるか、やってみようと思います。
本題
適当なディレクトリで
mix new example
します。
式はこちら
defmodule E do
def example( list ) do
list
|>Enum.map( & F.foo(&1) )
end
end
defmodule F do
def foo(list2) do
Enum.map(list2, fn x -> x * 2 end)
end
end
(あまり関数の命名が美しくないかもしれませんが、お許しください。。)
Enum.mapで各要素に対して下段のEnum.を呼び出し、それぞれの要素に2をかける、というものです。
最初はEnum.mapに対してEnum.mapを入れ込むことは適当じゃないのだろうか、という問いから出発し、
まずはでは式を作ってみよう、ということでこの式を作りました。
さっそくiexで[1,3,5]のリストを引数に関数exampleを呼び出してみます。
iex > E.example([1,3,5])
** (Protocol.UndefinedError) protocol Enumerable not implemented for 1. This protocol is implemented for: HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
(elixir) /private/tmp/elixir-20190202-21222-1ilp2g0/elixir-1.8.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) /private/tmp/elixir-20190202-21222-1ilp2g0/elixir-1.8.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3
(elixir) lib/enum.ex:3015: Enum.map/2
(elixir) lib/enum.ex:1327: Enum."-map/2-lists^map/1-0-"/2
?????????
"加算プロトコルは1のために実装されていない。このプロトコルは〜〜〜"
普通に整数のリストを与えても上記エラーが出ます。
iex > E.example([false , "first" ,123])
** (Protocol.UndefinedError) protocol Enumerable not implemented for false. This protocol is implemented for: HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
(elixir) /private/tmp/elixir-20190202-21222-1ilp2g0/elixir-1.8.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) /private/tmp/elixir-20190202-21222-1ilp2g0/elixir-1.8.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3
(elixir) lib/enum.ex:3015: Enum.map/2
(elixir) lib/enum.ex:1327: Enum."-map/2-lists^map/1-0-"/2
リストの中身を真偽値や文字列にしても同じエラーに阻まれました。
???????????
次いで、関数fooの動作確認をします。
iex > F.foo([1,3,5])
[2, 6, 10]
こちらは式のとおりリストの各要素にかける2をした値が返ってきました。
次いで、関数exampleにリストで包んだリスト、[[1, 2], [3, 4]]を与えてみます。
iex > E.example([[1, 2], [3, 4]])
[[2, 4], [6, 8]]
しっかりリストのなかの整数にすべて2をかけた値が返ってきました。
これは何を意味するのか?
ここで先生から重要なことを教わりました。
"Enum.mapにただの整数を渡すとエラーになる"
ということです。
つまり、Enum.mapにEnum.mapを入れ子にした関数では、
上段のEnum.mapで一度リストが各要素にバラされてしまうので、
"リストをリストで包んだデータ"を与えてやらねばならない
ということです。
ここからも先生の教えです。
この”リストをリストで包んだデータ”が何を意味するか。
これが意味するところのひとつは、表で表された数値データです。
[[1,2],[3,4]]は
|1|2|
|3|4|
のような表を表している。
このような表現を、数学では”行列"と呼ぶそうです。
また、いままでの数値を要素にしたリストを”ベクトル"と呼ぶ。
つまり、ベクトルを束ねたものが行列。
なお、リストのリストはC言語などでは"2次元配列"と呼ぶ。
つまり、
リスト=配列=ベクトル
リストのリスト=2次元配列=行列
というように対応する。
ここまで、先生の教えをうまく噛み砕けていないのですが、後から見返したときのために記述しておきます。
編集後記
前回IO.putsを入れ込んだところに自由に関数を当てはめることができる、また、Enum.mapのなかにEnum.mapを入れ込むことができる。
まだまだ、Elixirに限らずとも自分の自由な思考でコードを書くことに至らず、速いと信じていた自分の学習スピードに疑問を持つこともあります。
Qiitaドキュメントも、誤った記述をしてはならないようなセルフプレッシャーが強く、わからないこともうまく表現できないことがままあります。
が、必ず備忘録として役に立つ日があとで来るので、なるべくすまさず素直な書き方ができたらなと思います。
うまずたゆまず、頑張ります。
Kento Mizuno