本記事は「Elixir Advent Calendar 2021」の1日目です。
Elixir のリモートもくもく会autoracexでいつもお世話になっているオーサムさん(@torifukukaiou)の横の1日目がぽっかり空いていたので空気を読まずに頂戴いたしました
皆さんElixirを楽しんでますか
僕は今年ほぼ毎日、趣味のElixirに没頭しました
各種Elixirコミュニティの主催者様に感謝です
(Elixir.JPのslackはこちら)
ElixirによるIoT開発フレームワークNervesとラズパイにも取り組み、ありがたいことにNervesコアチームのフランクさんとコラボする機会も頂きました
さて、今日はそのフランクさんとElixir/Nervesプロジェクトに取り組んで学んだことをひとつ取り上げます
Enum
を使わない「Enum」です
あるときに、フランクさんと取り組んでいたプロジェクトでIPアドレスのリストを表示する機能を実装していました
例えば、以下のように、データを加工する必要があるとします(addrとnetmaskのペアだけ取り出す)
# Input
[
flags: [:up, :broadcast, :running, :multicast],
addr: {10, 0, 0, 202},
netmask: {255, 255, 255, 0},
broadaddr: {10, 0, 0, 202},
addr: {65152, 0, 0, 0, 47655, 60415, 65227, 8746},
netmask: {65535, 65535, 65535, 65535, 0, 0, 0, 0},
hwaddr: [184, 39, 235, 203, 34, 42]
]
# Output
[
{{10, 0, 0, 202}, {255, 255, 255, 0}},
{{65152, 0, 0, 0, 47655, 60415, 65227, 8746}, {65535, 65535, 65535, 65535, 0, 0, 0, 0}}
]
皆さんはどのようにデータを加工するでしょうか?
僕は、例えばこんな感じでEnum
モジュールの関数を組み合わせて、「完璧に関数プログラミングをしているつもり」になっていました
def extract_ifaddr_addresses1(kv_pairs) do
kv_pairs
|> Enum.filter(fn {k, _v} -> k in [:addr, :netmask] end)
|> Enum.map(fn {_k, v} -> v end)
|> Enum.chunk_every(2)
|> Enum.map(&List.to_tuple/1)
end
機能的には問題なくイゴいていたのですが、プルリクエストをフランクさんに見てもらってもなかなかOKがもらえません
当初、何に引っかかっているのか謎でしたが、フランクさんの代案を見せてもらって納得しました
def extract_ifaddr_addresses2(kv_pairs, acc \\ [])
def extract_ifaddr_addresses2([], acc), do: Enum.reverse(acc)
def extract_ifaddr_addresses2([{:addr, addr}, {:netmask, netmask} | rest], acc) do
extract_ifaddr_addresses2(rest, [{addr, netmask} | acc])
end
def extract_ifaddr_addresses2([_other | rest], acc) do
extract_ifaddr_addresses2(rest, acc)
end
更にもう一歩踏み込んでEnum
モジュールがなくても関数とパターンマッチだけで「Enum」できるんですね (厳密には一箇所Enum.reverse/1
が登場しますが)
目からウロコです
かっこいい
実際のコードはこちら
フランクさん曰く「Erlangではこれが普通だよ」ってことでした
どのスタイルが良いか、書きやすいか、読みやすいかは人それぞれで、状況にもよるとは思いますが、こういう考え方があるんだと個人的には衝撃的でした
Elixirに触れているとこのような新鮮な発見が多々あり、どんどんElixirにハマっていきます
今日は「NervesJP Advent Calendar 2021」にも参加したので、もしよろしければ御覧ください
明日は@torifukukaiouさんの「【Elixir】B - FizzBuzz Sum問題をEnum.map/2 |> Enum.filter/2 |> Enum.sum/1, Enum.reduce/3 を使って解く」です
引き続きEnum
の話になりそうですね