やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【レポート】第2回清流elixir勉強会in丸の内を開催しました

トピック

f:id:takamizawa46:20190413153340j:plain:w500
第2回清流elixir勉強会
昨日、無事に第2回の勉強会を開催させて頂きました
第1回目では方針を決める時間が長く、手を動かす時間が全くなかったのですが
今回の勉強会では「パイプ演算子を触る」というテーマで
普通に真面目に勉強しました(そこそこ真面目に

第1回では参加者は僕を含めて2人だったのですが、 今回、新たな方に参加して頂けまして3人での勉強会となりました

清流elixir-infomation
開催場所: 丸の内(愛知)
人数: 2 -> 3 update!!
コミュニティ参加人数 : 2 -> 4 update!!
20190413現在

第2回の活動内容

Enumについて

パイプ演算子を使ってデータをごちゃごちゃやりましょうっていうテーマで
どれだけelixirがデータ処理に対して強力なのかを体験しました

合わせてEnumモジュールについての知識が必要になりましたのでざっくりと解説しました
優しく始めるelixirのEnumと簡単な使用例の方でそこそこ詳しく解説してますので
参考にして頂ければ幸いです

f:id:takamizawa46:20190413153432j:plain:w400
Enumと列強可能なデータ構造について

Enumモジュールが使えるデータ構造は以下の3つ

  • リスト(list [])
  • レンジ(range n..n+m)
  • マップ(map %{})

このEnumとパイプ演算子の組み合わせがどれだけヤバいかを触ることに

パイプ演算子について

numbers = [1, 2, 3, 4, 5]

res = func1(numbers)
res = func2(res)
res = func3(res)
res = func4(res)
print(res) #最終的な目標値 

こんな感じである関数の戻り値を使って、次の関数の引数に何回も渡すことってわりとある気がします
ただ毎回resに戻り値いれて、次の関数の引数にいれてって単純にめんどくさいし、どこで何か起きてるのか後に分かりにくい

ただelixirにはパイプ演算子という強力な構文が用意されており、上記のようなことが簡単に行える

f:id:takamizawa46:20190413154645j:plain:w400
パイプ演算子について

numbers = [1,2,3,4,5]
res = Enum.map(numbers, fn ...)
        |> Enum.map(fn ...)
        |> Enum.map(fn ...)
        |> Enum.map(fn ...)
IO.inspect(res)

まるで工場のベルトコンベアのように順を追うことができて何がしたいのかがパッと見れば分かる(川の様との意見も

実際に試してみた

最初はシンプルな問題に取り組んでみた

data: 1...100(range)
1. 全ての要素を8倍
2. 500以上の要素を削除
3. 残りの要素を足し合わせる

data = 1..100
data
  |> Enum.map(fn x -> x * 8 end)
  |> Enum.filter(fn x -> x <= 500 end)
  |> Enum.sum() 

#result: 15624

ここではelixirの文法を確認しつつも、特に詰まる部分はなかった
しかし、あえてsumを使わずにreduceを使って実装してみることに(マゾ
reduce使えばできることってだいたいEnumに実装されているのでreduceの活躍できる場所は中々難しいです
reduceの詳細は優しく分かるEnumのreduce関数と簡単なサンプルで解説しています

data = 1..100
data
  |> Enum.map(fn x -> x * 8 end)
  |> Enum.filter(fn x -> x <= 500 end)
  |> Enum.reduce(fn x, accum -> x + accum end)

#result: 15624

提案を頂き、ついでにreduceで最大値も求めることに

data = 1..100
data
  |> Enum.map(fn x -> x * 8 end)
  |> Enum.filter(fn x -> x <= 500 end)
  |> Enum.reduce(fn x, accum -> if x < accum do accum else x end end)

#result: 496

続いて文字列にチャレンジ

data = ["apple", "banana", "peach", "grape", "orange", "strberry", "pineapple", "raspberry"]
1. 頭文字を大文字にする
2. "a"を除去する
3. "py"を単語の最後に結合する

ここで初めてStirngモジュールが登場し全員で公式ドキュメントを漁る
先頭の文字列を大文字に変換してくれるString.capitalizeたるものを発見しアガる

data
  |> Enum.map(fn str -> String.capitalize(str) end)

#result:
# ["Apple", "Banana", "Peach", "Grape", "Orange", "Strberry", "Pineapple", "Raspberry"]

つづいて"a"を文字列から削除するが、空文字に置き換えてしまえばいいことに気づき再びアガる

data
  |> Enum.map(fn str -> String.capitalize(str) end)
  |> Enum.map(fn str -> String.replace(str, "a", "") end)

#result:
#["Apple", "Bnn", "Pech", "Grpe", "Ornge", "Strberry", "Pinepple", "Rspberry"]

ここで重要なことに気づく。Aが消えてねーじゃん!!!! なんで先に大文字に変換したねん〜
ゴリ押しで解決する

data
  |> Enum.map(fn str -> String.capitalize(str) end)
  |> Enum.map(fn str -> String.replace(str, "a", "") end)
  |> Enum.map(fn str -> String.replace(str, "A", "") end)

#result:
#["pple", "Bnn", "Pech", "Grpe", "Ornge", "Strberry", "Pinepple", "Rspberry"]

最後に文字列の連結演算子 "a "<> "b" = "ab" をつかって"py"を付与する

data
  |> Enum.map(fn str -> String.capitalize(str) end)
  |> Enum.map(fn str -> String.replace(str, "a", "") end)
  |> Enum.map(fn str -> String.replace(str, "A", "") end)
  |> Enum.map(fn str -> str <> "py" end)

#result:
#["pplepy", "Bnnpy", "Pechpy", "Grpepy", "Orngepy", "Strberrypy", "Pinepplepy", "Rspberrypy"]

1mmも意味のない単語が出来上がる...

残念ながら時間切れ

マップを使った問題に取り組み始めた所でレンタル会議室の時間切れ...
結局ここまでしか触れませんでした(反省

マップを触るには先にパターンマッチ知りたいねという話になり
次回はパターンマッチについて勉強しようと思います 今回の勉強会では記事にしてない部分を含めて

  • Enumモジュールの触り方
  • パイプ演算子について
  • 公式ドキュメント最高だよね
  • _(アンダースコア)について
  • ifの書き方

等を共有しました
ワイワイ楽しく出来たと思います
次回はまた2週間後(4月下旬)に開催予定です

よろしければぜひconnpassからご参加ください