はじめに
こんにちは!
大好評の「Elixirでそげなことしよったちゃつまらんばい」シリーズ記事です。
所属するfukuoka.ex にちなみ、福岡のとある地域でつかわれる方言をシリーズ名に冠しています。
📝 この記事の要約
- Elixirの変数はイミュータブル(不変)である
- 無名関数内で外部変数を再代入しても元の値は変わらない
- 前の値を引き継ぐには
Enum.reduce/3
を使う - 他言語の知識で書こうとすると「つまらんばい」になる実例を紹介
今回はElixirの変数はイミュータブルであるため、他のプログラミング言語の知識で書こうとしても、「つまらんばい」を紹介します。
私自身は、「Elixr実践入門」 という書籍 の執筆にまでかかわらせていただくことができるまでにはなりましたが、学びはじめのころはこんな駄目なことをしていた思い出話でもあります。
日本全国あまねく地域の方に、伝わるとは思いますが、「そんなことしても駄目だぞ」ということです。
Elixirの変数はイミュータブル
まず、イミュータブル(不変)というのは、変数の値を変更できないことを意味します。例えば、以下のコードを見てください。
❌ 他言語の感覚で書いてしまう
a = 0
Enum.each(1..10, fn i ->
a = a + i
end)
IO.puts(a) # 出力は0
このコードを実行すると、結果は0になります。
1〜10を足した松井秀喜選手の背番号である55ではありません。
なぜでしょうか?
なぜ出力が0になるのか?
無名関数の中でa
を再代入しようとしていますが、Elixirではa
はイミュータブルです。つまり、無名関数のスコープ内でa
は常に初期値の0を参照します。これが「つまらんばい」な理由です。
どうすれば良いのか?
前の値を引き継ぐためには、Enum.reduce/3
を使います。この関数は、リストの各要素に対して累積計算を行うのに役立ちます。
Enum.reduce/3
の使い方
以下のコードを見てください。
⭕️ Enum.reduceを使って1から10までの合計を計算
total = Enum.reduce(1..10, 0, fn i, acc ->
acc + i
end)
IO.puts(total) # 出力は55
このコードでは、Enum.reduce/3
の第1引数が1から10の範囲、第2引数が初期値(0)、第3引数が無名関数です。この無名関数の引数は、現在の値i
と、前回の計算結果acc
です。これにより、acc
は前の値を持ち続け、最終的に合計を計算することができます。
ちなみに、Enum.reduce/3
を専門家の間では「畳み込み」と呼びます。
つまずきやすいポイント
- 無名関数のスコープ: 無名関数の中では、外部の変数を持ち込むことはできますが、変更することはできません。
- イミュータブルの理解: イミュータブルの概念は他の言語(特にオブジェクト指向言語)でのミュータブルな変数の扱いとは異なるため、最初は混乱するかもしれません。
実践的な使用例
Elixirはデータ処理や並行処理に適しています。ここでは、リストの合計だけでなく、特定の条件に基づくフィルタリングと合計を行う例を示します。
# 偶数の合計を計算する
even_sum = Enum.reduce(1..10, 0, fn i, acc ->
if rem(i, 2) == 0 do
acc + i # 偶数の場合だけ加算
else
acc # 奇数はそのまま
end
end)
IO.puts(even_sum) # 出力は30(2 + 4 + 6 + 8 + 10)
このコードでは、1から10までの数の中で偶数だけを合計しています。rem(i, 2) == 0
で偶数を判定し、条件に合う場合だけacc
に加算しています。
売上データの分析
上の例では実践的な使用例といいながら、「偶数の和」というまだ抽象的な例になってしまっていますが、表形式のようなデータがあったとしてある条件にあてはまるときだけ足すというようなことは現実でもよくありそうです。
実際のビジネスシーンでの応用例です。
# 売上データのリスト
sales_data = [
%{product: "ノートPC", category: "electronics", amount: 120000, month: 3},
%{product: "マウス", category: "electronics", amount: 3000, month: 3},
%{product: "本", category: "books", amount: 1500, month: 2},
%{product: "スマートフォン", category: "electronics", amount: 80000, month: 3},
%{product: "雑誌", category: "books", amount: 800, month: 3}
]
# 3月の電子機器の売上合計を計算
march_electronics_total = Enum.reduce(sales_data, 0, fn item, acc ->
if item.category == "electronics" and item.month == 3 do
acc + item.amount
else
acc
end
end)
IO.puts("3月の電子機器売上: #{march_electronics_total}円") # 203,000円
読者の皆さんへ
皆さんは、Elixirを学び始めた時にどんな「つまらんばい」を経験しましたか?
コメント欄で共有していただけると、他の初学者の方の参考になります!
まとめ
今回はElixirのイミュータブルな変数について学びました。イミュータブルの特性を理解することは、Elixirを効果的に使うための第一歩です。次に、Elixirの他の関数や並行処理の概念を学ぶことで、さらに深い理解を得ることができます。
次のステップとして、Enum.map/2
やEnum.filter/2
などの他の関数を試してみてください。それでは、楽しいElixirライフを!
自称・闘魂プログラマー。主な著書に『Elixir実戦入門』。私のAIは一味違う。Artificial Intelligenceではなく、Antonio Inokiさんの方のAI、つまり猪木さん。闘魂と共に、世界恒久平和の実現を目指してコードを書く。