fukuoka.ex代表のpiacereです
今回もご覧いただいて、ありがとうございます
「Elixirは何故whileやメンバー変数が無いの?」に答えてみる① で、Enum.sumを紹介しましたが、その内部実装が、Elixirを初めたばかりの方には、トリッキーな構文だったので、Elixirのコードを読む練習として、少し解説したいと思います
お知らせ:Elixirもくもく会(リモート参加OK、入門トラック有)を今日、開催です
「fukuoka.ex#14:Elixir/Phoenixもくもく会~入門もあるよ」を9/28(金)に開催します
今回から「Zoomによるリモート参加」を正式に受け付けるようになりましたので、福岡以外の都心や地方からでも参加できます(申し込みいただいたら、追ってZoom URLをconnpassメールでお送りします)
また、これまではElixir/Phoenix経験者を対象とした、もくもく会オンリーでしたが、今回から、入門者トラックも併設し、fukuoka.exアドバイザーズ/キャストに質問できるようにアップグレードしました
お申込みはコチラから
https://fukuokaex.connpass.com/event/100659/
Enum.sumはどう実装されているか?
こんなコードを別コラムで紹介しました
iex> [ 1, 3, 5 ] |> Enum.sum
9
Elixirでは、Enum.reduceを使って、こんな感じにも書けます
iex> [ 1, 3, 5 ] |> Enum.reduce( 0, fn( x, acc ) -> acc + x end )
9
Enum.sumの実装も、下記コードのように、Enum.reduceが基盤として使われています(Enumモジュール中の定義のため、reduceから「Enum.」が省略されています)
…
2428: def sum(enumerable) do
2429: reduce(enumerable, 0, &+/2)
2430: end
…
「アリティ」、Elixirの関数は引数の数で特定される
ここで「&+/2」が、Elixirを読み慣れていないと理解しにくいですが、これはKernelモジュールの「+」という2つの引数を取る関数に、上記のxとaccに相当するものを渡して呼び出すことを意味しています
この「/」と引数の個数で指定するのを「アリティ」と呼びます
以下のように書き換えると、理解しやすいかと思います
Enum.reduce( enumerable, 0, &( Kernel.+( &1, &2 ) ) )
なお、&1や&2は、&(~)で括った関数内に前から順に数字付きで参照できるようにする表記で、xやaccは、この表記によって展開されているので、冒頭のEnum.reduceで書いていた「fn( x, acc ) -> acc + x end」と同じになります
普段から使っているが、馴染みが薄いKernelモジュール
また、Kernelモジュールの「+」は、以下のような、普通の足し算でも呼び出されているルーチンのシンタックスシュガーです
iex> 1 + 2
3
iex> Kernel.+( 1, 2 )
3
ですので、以下のようにも書けます
Enum.reduce( enumerable, 0, &( &1 + &2 ) )
なお個人的には、「Kernel.~」の表記は、可読性が落ちるため、使わないように気をつけています
特に、パイプで繋げてると、使いたくなる誘惑に駆られますが、そこは他の方が見ることも意識して、フレンドリーなコードを守ります