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の連想配列版。ランダムに選ばれたキーが削除され、削除されたキーと値が返される。