LoginSignup
17
1

ElixirでEnumを使わずEnumする

Last updated at Posted at 2021-11-30

本記事は「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の話になりそうですね

17
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
1