tableに機能を追加する。
table.lua
-- table.lua -- tableを扱うライブラリ -- arrayも追加する。 do array = {} -- テーブルのシンプルなコピー -- そのままコピーするので、値にテーブルがあったりしたらそれを参照渡ししてしまう。 table.copy = function(t) local new = {} for k, v in pairs(t) do new[k] = v end return new end array.copy = table.copy -- テーブルのクローン -- 値がnumber, string, function等はそのまま代入。 -- tableがあれば、それをさらにクローンする。 table.clone = function(t) local new = {} for k, v in pairs(t) do if type(v) == "table" then new[k] = table.clone(v) else new[k] = v end end return new end array.clone = table.clone -- テーブルの移し替え -- テーブルaからテーブルbに中身を全部入れ替える。 -- テーブル変数の実体は変更せずに、中身だけ変更したい時に有効。 -- メタテーブルについてはさわっていないので、注意。 -- fromには影響はない。 table.move = function(from, to) for k, v in pairs(to) do rawset(to, k, nil) end for k, v in pairs(from) do rawset(to, k, v ) end return to end array.move = table.move -- ミックスソートで使う比較関数群 local compare_box = {} compare_box.number = {} compare_box.number.number = function(a, b) return a<b end compare_box.number.string = function() return true end compare_box.string = {} compare_box.string.number = function() return false end compare_box.string.string = compare_box.number.number local compare = function(a, b) return compare_box[type(a)][type(b)](a, b) end -- ミックスソート。数値と文字列の入り混じった配列をソートする。 -- 数値の後に文字列がきて、数値は数値同士、文字列は文字列同士並ぶ。 array.mix_sort = function(t) table.sort(t, compare) end -- そのテーブルの持っているキーをテーブルの値にして返す。 table.keys = function(t) local new = {} for k, v in pairs(t) do new[1+#new] = k end table.sort(new, compare) return new end array.keys = table.keys -- そのテーブルのnumber型キーのうち、最大値を返す。 -- 間を飛ばした配列型テーブルに使うと有効。 -- 文字列キーが混じっていても無視する。 table.last = function(t) local max = 0 for k, v in pairs(t) do if type(k)=="number" then if max<k then max=k end end end return max end array.last = table.last -- 配列の空いてる部分を指定した何かで埋める。 array.fill = function(t, v) local max = array.last(t) if max==0 then return t end for i=1, max do if type(t[i])=="nil" then t[i] = v end end return t end -- 二分法 -- 引数に関数を与え、 -- 配列型テーブルの各要素を二つの配列型テーブルに分けて返す。順番は守られる。 array.dichotomy = function(t, func) local res1 = {} local res2 = {} for i, v in ipairs(t) do if func(v) then res1[1+#res1] = v else res2[1+#res2] = v end end return res1, res2 end -- テーブル版二分法 -- 順番は守られない。 table.dichotomy = function(t, func) local res1 = {} local res2 = {} for i, v in pairs(t) do if func(v) then res1[i] = v else res2[i] = v end end return res1, res2 end -- フィルタリング array.filter = function(t, func) local res = array.dichotomy(t, func) return res end table.filter = function(t, func) local res = table.dichotomy(t, func) return res end -- ユニーク -- 配列変数に対して使う。重複する値があったら、二度目以降は削除してコピーを返す。 array.unique = function(t) local check = {} local res = {} for i, v in ipairs(t) do if not(check[v]) then check[v] = true res[1+#res] = v end end return res end -- 複数の配列を接続する。 -- 引数には複数の配列を与える。先頭の配列に、残りの全ての配列の全要素を足す。 -- その後、先頭の配列を返す。 -- 先頭の配列だけが影響を受け、他の配列は影響を受けない。 array.join = function(...) local args = {...} local res = table.remove(args, 1) -- 先頭を取得 for i, arg in ipairs(args) do if type(arg)=="table" then for k, v in ipairs(arg) do res[1+#res] = v end else res[1+#res] = arg end end return res end -- その配列の中からランダムに一つ抜き出して返す。 -- table.removeを使っているので、抜いた分詰められる。 -- 第二引数には関数を指定できる。指定できなければ、math.randomが使われる。 -- 第二引数の関数は、math.randomに引数を一つ指定した場合と同様の動作が期待されている。 array.pick = function(t, func) if 0==#t then return nil end func = func or math.random return table.remove(t, func(#t)) end -- array.pickの連想配列版 -- 返り値がk, vになっている点が違う。 table.pick = function(t, func) local k = array.pick(table.keys(t), func) local v = t[k] t[k] = nil return k, v end end
table.copy/array.copy
テーブルをコピーします。
同じ内容の新規テーブルを返しますが、値を単純に代入しているだけなので、number, string, boolean型以外の値は、参照渡しになります。(コピー元とコピー先で同じものを共有しているので、どちらかで変更されたら、反対側でも変更される)
local copy = table.copy(t)
table.clone/array.clone
local clone = table.clone(t)
table.copyの発展版で、値にテーブルがあった場合は、それを再帰的にコピーしていきます。普通はこちらを使います。
オブジェクトは互いに独立しているので、副作用を抑えることができます。
table.move/array.move
local from = {} local to = {} table.move(from, to)
テーブルfromの持つ内容を、そのままテーブルtoにコピーします。こちらは全て参照渡しになります。
array.mix_sort
table.sortの一変形です。
array.mix_sort(t)
通常のtable.sortで、比較関数を与えない場合、値に文字列と数値(あるいはそれ以外)が混在していると、エラーを出してしまいます。
これはそれ(ただし、numberとstring型に限定)を考慮して並べ替えます。数値・文字列の順番になります。
table.keys/array.keys
local key = table.keys(t)
そのテーブルのもつキーを、調べ、新しい配列テーブルの値として返します。
返ってきた新しいテーブルは、table.mix_sortするとよいかも知れません。
table.last/array.last
local last_key_of_number = table.last(t)
その配列の数値キーの最大値を返します。#演算子よりも高機能ですが、遅いです。
- 調べる対象のテーブルが配列型の場合、間が開いていても最大値を割り出す。
- 文字列キーが混入していた場合、それは無視されます。
- 文字列キーしか存在しない場合は、0が返ってきます。
array.fill
歯抜け状態のキーを持つ配列に対し、開いている部分を特定のもので埋めます。
local t = {} t[1] = 1 t[3] = 1 -- この時点で、[1, nil, 3] array.fill(t, true) -- [1, true, 3]
array.dichotomy
第二引数に関数を与えます。この関数には、配列の一つ一つの要素が引数に与えられ、真偽値を返すことを期待されています。
その結果により、trueが返ってきた要素とfalseが返ってきた要素に分け、二つのテーブルを返します。
local t = {1, 2, 3} local even, odd = array.dichotomy(t, function(num) return (0 == num % 2) end) -- 偶数と奇数に分ける。
ちなみに、boolean関係を整備してみた。 - 永字八法のtorfを引数に与えると、ランダムに分けることができますし、flipflopを与えると順番に二つに分けることができます。
table.dichotomy
array.dichotomyのテーブル版。
array.filter/table.filter
array.dichotomyと同じように使いますが、返ってくるテーブルは一つだけで、真のグループのみです。
array.unique
配列の要素で重複分を省いてその分前に詰めた新しいテーブルを返します。
array.join
引数に複数の配列を与え、第一の配列に残りの全ての配列の要素を順番に詰めていきます。
array.pick/table.pick
array.pickは、その配列の中からランダムに選んで一つ返す。table.removeを使っているので、元の配列は一つ短くなる。
第二引数に関数を指定することができる。指定しなければ、math.randomが使われる。
関数は、math.random(#t)と同様の動作を期待されている。
table.pickはarray.pickの連想配列版。ランダムに選ばれたキーが削除され、削除されたキーと値が返される。