Lua#9 Luaで連想配列としてのテーブルを用いてデータを扱う

Last modified: 2021-07-22

おみくじ/複数の文字列の内の1つを無作為に選択する” ではテーブルを配列として用いていたが、ここでは “連想配列” という形でデータを格納したり取り出したりする。

解説

配列としてのテーブルの扱いをおさらい

過去に扱ったように、配列として扱うテーブルでは、データが単純に並んでいるだけの形をとる。 中の要素には1から始まる添え字を用いて読み書きする。

> mon = {'Jan', 'Feb', 'Mar'}              -- 配列として初期化
> print(mon[2])                            -- 2番目の要素を表示
Feb
> mon[2] = 'February'                      -- 2番目の要素を書き換え

ループを用いて各要素に対して処理を行うにはfor文でipairs()を用いる。

> for _, x in ipairs(mon) do print(x) end  -- 各要素を順に表示
Jan
February
Mar

連想配列としてのテーブル

これに対して、連想配列として扱うテーブルでは、順番を問わない形で各データ(値)を文字列に関連付けて格納していく。

つまり

  • 値を読み書きするための文字列
  • 対応する値

が関連付けられつつ、その組(ペア)が格納される。

Lua以外の言語にも同様の仕組みや型が用意されているものがある。例えばPerl(ハッシュ),Python(辞書型),C++(std::unordered_mapまたはstd::map),Rust(std::collections::HashMapまたはstd::collections::BTreeMap)など。

初期化時の値の指定

初期化時に値を指定する場合は[要素名]=[初期値]形式をコンマ区切りで記述する。

> pos = {x=114, y=514, z=810}  -- 初期値x,y,zを設定して連想配列を作成

値の読み書き

値の読み書きは添え字ではなく、2通りの書き方のいずれかで要素名を与えて行う。

  • [連想配列].[要素名]: ドット記号の後ろに要素名をそのまま記述
  • [連想配列]['[要素名]']: 要素名部分を角括弧で囲む形で、要素名に変数を用いるときに使える
> print(pos.x)     -- ドットを用いる形式でxの内容を表示
114
> print(pos['y'])  -- 角括弧を用いる方式でyの内容を表示
514
> key = 'z'        -- 要素名を入れる変数を用意
> pos[key] = 931   -- その要素名の変数を用いる形でzの新しい内容を書き込む

対応するデータのない要素名を指定して値を取り出そうとすると、取り出される値は特殊なnilという値(文字列ではない)になる。

> key = 'test'     -- この時点で存在しない要素名にしてみる
> print(pos[key])  -- 名前testに対応した値がないので nil となる
nil

対応するデータのない要素名を指定して書き込みを行うと、新しくその要素名に対して値が格納される。

> pos[key] = 666
> print(pos['test'])
666

各要素に対してループ処理

ループを用いて各要素に処理を行うにはfor文でpairs()を用いる。

このとき、取り出される “要素名と値” の組の順番は決まっていないので、取り出される順番を想定・期待するようなプログラムを書いてはいけない。
公式のLuaインタプリタ(luaコマンド)の実装では実行のたびにペアが取り出される順番は変わる。

(出力の順番は不定)
> for key, val in pairs(pos) do print(("%s:%s"):format(key, val)) end
z:931
test:666
x:114
y:514

中身が空かどうかをチェックする

next()の括弧内に連想配列としてのテーブルを指定して呼び出して戻り値がnilかどうかで中身が空かのチェックができる。

(空ではない場合)
> next(pos) == nil
false

(空の場合)
> empty={}
> next(empty) == nil
true

コード例

以上の挙動を確認するための例が以下となる。

[任意]ファイル名:009_associative-arrays.lua エンコーディング:UTF-8
#! /usr/bin/lua
-- -*- coding: utf-8 -*-

--[[--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --
動作確認バージョン: 5.4, JIT2.1
--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --]]

-----
----- 連想配列 (文字列を渡して、それに対応する値を読み書きする型)としての
----- テーブルの扱い
-----

-- 連想配列としてのテーブルの読み書き
do
    -- テーブル型は連想配列として使用できる
    -- 変数の値を {} とすることで空のテーブルを作成できる
    local envar = {}

    -- キー文字列 "PATH" に対応する値を環境変数PATHの値にする
    -- os.getenv()は指定した名前の環境変数の値を取得する
    -- (環境変数が未定義の場合は特殊な値 nil が得られる)
    envar['PATH'] = os.getenv('PATH')
    -- 角括弧にキー文字列を入れる形以外に "." の後ろにキー文字列を付ける形もある
    envar.LANG = os.getenv('LANG')
    envar.DISPLAY = os.getenv('DISPLAY')

    -- 入れた値を個別に取り出して表示
    print(('LANG: %s'):format(envar['LANG']))
    print(('PATH: %s'):format(envar.PATH))
    print(('DISPLAY: %s'):format(envar.DISPLAY))
    -- "FOO" に対する値が未定義の場合は特殊な値 nil になる
    print(('FOO: %s'):format(envar.FOO))  -- nil の場合も文字列に変換して表示
end

print(('-'):rep(80))  -- 表示の区切り

-- 連想配列の各要素に対するループ処理
do
    -- 初期値を入れた形の連想配列としてのテーブル
    local fruits = {
        apple='リンゴ',
        orange='オレンジ',
        cherry='サクランボ',
    }

    -- pairs()とfor文を用いるとキー文字列と対応する値の組(ペア)を参照しながら
    -- ループ処理が行える
    -- ただし **組が得られる順番は決まっていないのでその順番に依存してはいけない**
    -- 実際、Lua公式の実装では表示される順番は実行のたびに異なる
    for key, val in pairs(fruits) do
        print(('%s は %s です'):format(key, val))
    end
end

print(('-'):rep(80))

-- テーブルが空かどうかのチェック
do
    local tbl_a = {}     -- 空
    local tbl_b = {a=7}  -- 空ではない
    -- "next([テーブル])" の戻り値が nil の場合は空のテーブルとなる
    if not next(tbl_a) then
        print('"{}" で初期化したテーブルは空です')                 -- 実行される
    else
        print('"{}" で初期化したテーブルは空ではありません')       -- ここは実行されない
    end
    -- "next([テーブル])" の戻り値が nil でない場合は空のテーブルではない
    if not next(tbl_b) then
        print('"{a=7}" で初期化したテーブルは空です')            -- ここは実行されない
    else
        print('"{a=7}" で初期化したテーブルは空ではありません')  -- 実行される
    end
end

-- 終了

下は実行結果。一部は環境変数によって表示内容が異なり、一部は実行のたびに表示順が異なる。

LANG: [環境変数LANGの内容または nil]
PATH: [環境変数PATHの内容または nil]
DISPLAY: [環境変数DISPLAYの内容または nil]
FOO: [環境変数FOOの内容または nil]
--------------------------------------------------------------------------------
apple は リンゴ です
orange は オレンジ です
cherry は サクランボ です
--------------------------------------------------------------------------------
"{}" で初期化したテーブルは空です
"{a=7}" で初期化したテーブルは空ではありません
  • 使用したバージョン
    • Lua 5.4.2
    • LuaJIT 2.1.0 Beta 3