Elixirでは、タプルはデータをまとめるデータ構造のひとつで、どんな型の要素でも決まった個数だけ保持できます。ただし、それぞれの要素に名前はありません。タプルは、メモリからの読み取りが多い処理でよく使われます。要素への読み取りアクセスが定数時間で済むからです。一方、要素を動的に追加したり削除したりする場面では、ふつう使いません。既存のタプルを変更するのではなく新しいタプルを作ることになり、そのぶんメモリを前もって確保しなければならないからです。
実際には、タプルは波括弧({})を使って作ります。タプルの要素には、elem/2関数と0から始まるインデックスを使って個別にアクセスできます。
empty_tuple = {}
one_element_tuple = {1}
multiple_element_tuple = {1, :a, "hello"}
elem(multiple_element_tuple, 2)
# => "hello"
タプルは、まとまった情報を表すのによく使われます。
Float.ratio(0.25)
# => {1, 4} indicating the numerator and denominator of the fraction ¼
パターンマッチングは、断定的で慣用的なElixirのコードで中心的に使われます。=/2は代入演算子ではなくマッチ演算子と呼ばれているのを覚えているでしょうか。マッチ演算子を使うとき、左辺のパターンが右辺とマッチすれば、左辺の変数が束縛され、右辺の値が返ります。マッチしなければ、MatchErrorが発生します。
2 = 2
# => 2
# Literals can be matched if they are the same
2 = 3
# => ** (MatchError) no match of right hand side value: 3
{_, denominator} = Float.ratio(0.25)
# => {1, 4}
# the variable `denominator` is bound to the value 4
マッチは右辺から左辺へと行われることを覚えておきましょう。
最後の例で、パターンマッチに変数が必要ない場合は、_を参照して捨てることができます。_で始まる変数は、ランタイムには追跡されません。
パターンマッチングは、複数の関数節を作るときにも役立つ道具です。関数の引数に対してパターンマッチングを使うと、どの関数節を呼び出すかが決まります。ファイルの先頭から順に見ていき、最初にマッチした節が選ばれます。変数は関数ヘッドで束縛し、関数本体で使えます。
defmodule Example do
def named_function(:a = variable_a) do
{variable_a, 1}
end
def named_function(:b = variable_b) do
{variable_b, 2}
end
end
Example.named_function(:a)
# => {:a, 1}
Example.named_function(:b)
# => {:b, 2}
Example.named_function(:c)
# => ** (FunctionClauseError) no function clause matching in Example.named_function/1
友達のためにクッキーを焼こうと準備しているうちに、レシピで使われているいくつかの分量を変換しなければならないことに気づきました。メートル法にしか慣れていないので、アメリカのお菓子作りでよく使われる分量をミリリットル(mL)に変換する方法を、自分のために考えておく必要があります。
解答には、次の変換表を使ってください。
| 変換する単位 | 体積 | ミリリットル(mL) |
|---|---|---|
| mL | 1 | 1 |
| アメリカのカップ | 1 | 240 |
| アメリカの液量オンス | 1 | 30 |
| アメリカの小さじ | 1 | 5 |
| アメリカの大さじ | 1 | 15 |
プログラミングの腕を磨いているなら、表にあるどの単位から別の単位へも(同じ単位同士へも)変換しやすくするために、ミリリットルを中間の単位として使うのがよいでしょう。
KitchenCalculator.get_volume/1関数を実装しましょう。量のペアのタプルが与えられたら、その数値の部分だけを返します。
KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0
KitchenCalculator.to_milliliter/1関数を実装しましょう。量のペアのタプルが与えられたら、変換表を使ってその体積をミリリットルに変換します。
複数の関数節とパターンマッチングを使って、各単位の関数を作りましょう。それぞれの単位を表すアトムは:cup、:fluid_ounce、:teaspoon、:tablespoon、:milliliterです。変換結果はタプルに包んで返します。
KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}
KitchenCalculator.from_milliliter/2関数を実装しましょう。量のペアのタプルと変換したい単位が与えられたら、変換表を使ってその体積を目的の単位に変換します。
複数の関数節とパターンマッチングを使って、各単位の関数を作りましょう。それぞれの単位を表すアトムは:cup、:fluid_ounce、:teaspoon、:tablespoon、:milliliterです。
KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}
KitchenCalculator.convert/2関数を実装しましょう。量のペアのタプルと変換したい単位が与えられたら、与えられた体積を目的の単位に変換します。
KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}