こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はマップリストのデータの操作についてEnumチートシートを写経して学んだことをまとめます。
本記事はaccumulating(畳み込み処理をする関数群)についてです。
目的
マップリストの値の操作ができるように様々な関数を知りたい。
実行環境
Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.14.3
Erlang v26.0.2
前提
本シリーズは下記でscaffold生成されたPhoenix+LiveViewのプロジェクトを使用しています。
mix phx.new dec23
cd dec23
mix ecto.create
mix phx.gen.live Users User users name:string age:integer
# 生成されたroutingをrouter.exに記載
mix ecto.migrate
元データ
iex(1)> users = Dec23.Users.list_users
[debug] QUERY OK source="users" db=1.7ms idle=1572.5ms
SELECT u0."id", u0."name", u0."age", u0."inserted_at", u0."updated_at" FROM "users" AS u0 []
↳ :erl_eval.do_apply/7, at: erl_eval.erl:746
[
%Dec23.Users.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 1,
name: "santa",
age: 100,
inserted_at: ~U[2023-12-23 09:06:44Z],
updated_at: ~U[2023-12-23 09:06:44Z]
},
%Dec23.Users.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 2,
name: "reindeer",
age: 55,
inserted_at: ~U[2023-12-23 09:07:18Z],
updated_at: ~U[2023-12-23 09:07:18Z]
},
%Dec23.Users.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 3,
name: "dwarf",
age: 39,
inserted_at: ~U[2023-12-23 09:07:44Z],
updated_at: ~U[2023-12-23 09:07:44Z]
}
]
Enumチートシートとは?
Enumチートシートというものがあります。
下記のマップリストを例にしたチートシートです。
幸運なことに元データと同じデータ構造をしています!
cart = [
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
というわけでこれを写経してみました。
Enum.reduce
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。
・処理結果を新しいaccとして保ち、次のイテレーションに進む。
お手本の写経
Enum.reduce(cart, 0, fn item, acc ->
item.count + acc
end)
10
自分のケースでの演習
users |> Enum.reduce(0, fn user, acc ->
user.age + acc
end)
194
Enum.map_reduce
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。関数処理の結果は{result,acc}
の2つの要素をもつタプルの形式で出力される必要がある。
・上記で出力された{result,acc}
のaccを結果を新しいaccとして保ち、次のイテレーションに進む。
お手本の写経
Enum.map_reduce(cart, 0, fn item, acc ->
{item.fruit, item.count + acc}
end)
{["apple", "banana", "orange"], 10}
自分のケースでの演習
Enum.map_reduce(users, 0, fn user, acc ->
{user.name, user.age + acc}
end)
{["santa", "reindeer", "dwarf"], 194}
Enum.scan
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。
・処理結果に対して以下の処理を行う
├出力結果となるリストの要素にする
└かつ、新しいaccとして保ち、次のイテレーションに進む。1
お手本の写経
Enum.scan(cart, 0, fn item, acc ->
item.count + acc
end)
[3, 4, 10]
Enum.scan(1..5, 0, &(&1 + &2))
[1, 3, 6, 10, 15]
自分のケースでの演習
users |> Enum.scan(0, fn user, acc ->
user.age + acc
end)
[100, 155, 194]
Enum.reduce_while
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
第三引数で条件分岐を書く。条件分岐の結果はの下記のいずれかで出力される必要がある。
1.{:halt, acc}
...畳み込み処理を停止する場合
→ 関数処理をせずにその時点でのaccを出力して処理を停止する。
2.{:cont, acc}
...畳み込み処理を継続する場合
→ acc部分に関数処理を書く。
→上記関数処理で出力された{:cont, acc}
のaccを結果を新しいaccとして保ち、次のイテレーションに進む。
お手本の写経
Enum.reduce_while(cart, 0, fn item, acc ->
if item.fruit == "orange" do
{:halt, acc}
else
{:cont, item.count + acc}
end
end)
4
Enum.reduce_while(1..10, 0, fn x, acc ->
if x >= 5 do
{:halt, acc}
else
{:cont, acc + x}
end
end)
10 #x = 5 のときのイテレーションで{:halt, acc}に分岐する。
自分のケースでの演習
users |> Enum.reduce_while(0, fn user, acc ->
if user.name == "dwarf" do
{:halt, acc}
else
{:cont, user.age + acc}
end
end)
155 #user.name = "dwarf" のときのイテレーションで{:halt, acc}に分岐する。
内包表記(for文)
reduce: acc
の書き方でaccの初期値を設定する。
<- の右側の要素を順番に取り出す。
取り出した各要素にdo endブロック内の処理をする。
処理結果を新しいaccとして保ち、次のイテレーションに進む。
お手本の写経
for item <- cart, reduce: 0 do
acc -> item.count + acc
end
10
自分のケースでの演習
for user <- users, reduce: 0 do
acc -> user.age + acc
end
194
内包表記(for文)とfilteringの合わせ技
reduce: acc
の書き方でaccの初期値を設定する。
<- の右側の要素を順番に取り出す。
do
の前に書かれた条件にヒットした要素にのみ絞り込み、do endブロック内の処理をする。
処理結果を新しいaccとして保ち、次のイテレーションに進む。
お手本の写経
for item <- cart, item.fruit =~ "e", reduce: 0 do
acc -> item.count + acc
end
9
自分のケースでの演習
for user <- users, user.name =~ "a", reduce: 0 do
acc -> user.age + acc
end
139
~Elixirの国のご案内~
↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます
↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。
↓We Are The Alchemists, my friends!2
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。3
-
Enum.reduceと違って途中のaccが要素として出力されてくる。 ↩
-
@torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。 ↩