マップは、Elixirで情報をキーと値のペアで保存するためのデータ構造です。他の言語では、連想配列(PHP)、ハッシュ(Perl 5、Raku)、辞書(Python)と呼ばれることもあります。
キーと値にはどんなデータ型でも使えますが、キーがアトムのときは省略した書き方を使えます。マップは、アクセスや返却のときにエントリの順序を保証しません。
空のマップは、単に%{}と書いて宣言します。マップリテラルに要素を追加したいときは、2つの書き方があります。
# If the key is an atom:
%{atom_key: 1}
# If the key is a different type:
%{1 => :atom_value}
# You can even mix these if the atom form comes second:
%{"first_form" => :a, atom_form: :b}
決まった書き方が定められているわけではありませんが、キーと値のリテラルのペアは一貫した書き方で表すようにしましょう。
Mapモジュールの関数Elixirには、マップを扱うための関数が_Mapモジュール_にたくさん用意されています。_Mapモジュール_の関数の中には、マップの変換を助けるために、_無名関数_を引数として渡す必要があるものもあります。
Elixirでは、モジュール属性を定義して、関数の中で定数として使うことができます。
defmodule Example do
# Defines the attribute as the value 1
@constant_number 1
def example_value() do
# Returns the value 1
@constant_number
end
end
モジュールの定数を定義するために使う場合、属性にはコンパイル時に評価できる任意の式を書けます。コンパイル後は、モジュール属性はコンパイル中に展開されてしまうため、アクセスできません。これは、Cなどの言語で定義するマクロに似ています。
この演習では、地元のゲームセンターで一番人気のゲームのハイスコアを記録しておく方法を実装します。
新しいハイスコアのマップを作るには、HighScore.new/0関数を定義します。この関数は引数を取らず、新しい空のハイスコアのマップを返します。
HighScore.new()
# => %{}
ハイスコアのマップにプレイヤーを追加するには、HighScore.add_player/3を定義します。これは3つの引数を取る関数です。
デフォルトの初期スコアは、モジュール属性に保存します。あとでもう一度必要になります。
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas")
# => %{"Dave Thomas" => 0}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"Dave Thomas" => 0, "José Valim"=> 486_373}
スコアのマップからプレイヤーを削除するには、HighScore.remove_player/2を定義します。これは2つの引数を取ります。
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas")
# => %{"Dave Thomas" => 0}
score_map = HighScore.remove_player(score_map, "Dave Thomas")
# => %{}
プレイヤーのスコアをリセットするには、HighScore.reset_score/2を定義します。これは2つの引数を取ります。
この関数は、プレイヤーがスコアを持っていない場合でも動作する必要があります。
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"José Valim"=> 486_373}
score_map = HighScore.reset_score(score_map, "José Valim")
# => %{"José Valim"=> 0}
以前のスコアに加算してプレイヤーのスコアを更新するには、HighScore.update_score/3を定義します。これは3つの引数を取ります。
この関数は、プレイヤーが以前のスコアを持っていない場合でも動作する必要があります。その場合は、以前のスコアが0であるとみなします。
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"José Valim"=> 486_373}
score_map = HighScore.update_score(score_map, "José Valim", 5)
# => %{"José Valim"=> 486_378}
プレイヤーの配列を取得するには、HighScore.get_players/1を定義します。これは1つの引数を取ります。
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas", 2_374)
# => %{"Dave Thomas" => 2_374}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"Dave Thomas" => 2_374, "José Valim"=> 486_373}
HighScore.get_players(score_map)
# => ["Dave Thomas", "José Valim"]