LoginSignup
8
1

Enumチートシートからマップリストの値の操作を学ぶ⑤ - accumulating

Last updated at Posted at 2023-12-24

こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はマップリストのデータの操作についてEnumチートシートを写経して学んだことをまとめます。
本記事はaccumulating(畳み込み処理をする関数群)についてです。

目的

マップリストの値の操作ができるように様々な関数を知りたい。

実行環境

Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.14.3
Erlang v26.0.2

前提

本シリーズは下記でscaffold生成されたPhoenix+LiveViewのプロジェクトを使用しています。

bash
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
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として保ち、次のイテレーションに進む。

お手本の写経

iex
Enum.reduce(cart, 0, fn item, acc ->
  item.count + acc
end)
10

自分のケースでの演習

iex
users |> Enum.reduce(0, fn user, acc ->
  user.age + acc
end)
194

Enum.map_reduce

accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。関数処理の結果は{result,acc}の2つの要素をもつタプルの形式で出力される必要がある。
・上記で出力された{result,acc}のaccを結果を新しいaccとして保ち、次のイテレーションに進む。

お手本の写経

iex
Enum.map_reduce(cart, 0, fn item, acc ->
  {item.fruit, item.count + acc}
end)
{["apple", "banana", "orange"], 10}

自分のケースでの演習

iex
Enum.map_reduce(users, 0, fn user, acc -> 
 {user.name, user.age + acc}
end)
{["santa", "reindeer", "dwarf"], 194}

Enum.scan

accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。
・処理結果に対して以下の処理を行う
 ├出力結果となるリストの要素にする
 └かつ、新しいaccとして保ち、次のイテレーションに進む。1

お手本の写経

iex
Enum.scan(cart, 0, fn item, acc ->
  item.count + acc
end)
[3, 4, 10]
iex
Enum.scan(1..5, 0, &(&1 + &2))
[1, 3, 6, 10, 15]

自分のケースでの演習

iex
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として保ち、次のイテレーションに進む。

お手本の写経

iex
Enum.reduce_while(cart, 0, fn item, acc ->
  if item.fruit == "orange" do
    {:halt, acc}
  else
    {:cont, item.count + acc}
  end
end)
4
iex
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}に分岐する。

自分のケースでの演習

iex
 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として保ち、次のイテレーションに進む。

お手本の写経

iex
for item <- cart, reduce: 0 do
  acc -> item.count + acc
end
10

自分のケースでの演習

iex
 for user <- users, reduce: 0 do
   acc -> user.age + acc
end
194

内包表記(for文)とfilteringの合わせ技

reduce: accの書き方でaccの初期値を設定する。
<- の右側の要素を順番に取り出す。
doの前に書かれた条件にヒットした要素にのみ絞り込み、do endブロック内の処理をする。
処理結果を新しいaccとして保ち、次のイテレーションに進む。

お手本の写経

iex
for item <- cart, item.fruit =~ "e", reduce: 0 do
  acc -> item.count + acc
end
9

自分のケースでの演習

iex
for user <- users, user.name =~ "a", reduce: 0 do
  acc -> user.age + acc
end
139

~Elixirの国のご案内~

↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。

We Are The Alchemists, my friends!:bouquet:2
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。3

  1. Enum.reduceと違って途中のaccが要素として出力されてくる。

  2. @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。

  3. @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中!

8
1
0

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
8
1