Lua ではテーブル同士の比較は同一比較(アドレス比較)になり、内容で比較することはできない。
tamale.lua を使うと、
『内容、構造を調べ、当てはまるなら、続く文(ブロック)を実行する』
ができる
[注意]このライブラリはもうずっとメンテナンスされていないため、lua5.2 から非対応の古いライブラリだが、
module 宣言を消し、return するテーブルを作ってそれに関数をはやしてゆけばlua5.4でも使うことができる
サンプルコード
2048でタイルを左に詰めるコード(yuescript)
(lume.sliceはstring.subの配列版)
(...はテーブルを展開する)
.moon
moveLeft = (ss)->
recur, a = nil, tamale.var'a' -- matcher自体を再帰関数にするためには前方宣言が必要。
recur = tamale.matcher{
{[], []}
{[0,], partial: true, (c)-> [...recur(lume.slice(c.input, 2)), 0]}
{[a, a], partial: true, (c)-> [c.a*2, ...recur(lume.slice(c.input, 3)), 0]}
{[a, 0, a], partial: true, (c)-> [c.a*2, ...recur(lume.slice(c.input, 4)), 0, 0]}
{[a, 0, 0, a], (c)-> [c.a*2, 0, 0, 0]}
{[a,], partial: true, (c)-> [c.a, ...recur(lume.slice(c.input, 2))]} -- lume.concatで{}を扱うとそのままつける?バグがある?
}
[recur(s) for s in *ss]
ルール集の記述
- matcher にルール集(配列)を渡すと関数を返す
上から(先頭から)順に調べる - else 節の表記法はない(
[], partial: true
で代用)(->true
でも)。else が何もしないなら省略できる - ルールからmatcherを再帰呼出しをするには前方宣言が必要
その中の一つのルールの記述
- 一つのルールは
{[1] = パターン, [2] = 実行する関数, partial = true}
のように配列に付加情報を同居させる形 - 数値などのほか
tamale.var'x'
で変数を定義、キャプチャできる -
when = condition
でガード節を付けられる -
partial = true
で一部のキーだけを調べてパターンに当てはまるか判断するようになる
luaでは配列を記述することは{[1] = .. , [2] = ..}
というキーがあることと同じなので、パターンに配列を書くことは『先頭から(パターンに記述した配列の長さ分)調べる』という意味になる - 実行する関数はキャプチャしたものを引数として受け取る
-
local a = tamale.var'a'
とすると、パターン中で使え、capture.a
で受け取れる -
capture.input
でmatch関数に渡されたテーブルを受け取れる
-
その他のパターンの書き方
- tamale.P で正規表現での比較がある
- 関数: マッチ関数に渡された値(input)を引数に呼ばれ真であれば成立
その他の付加情報
- ids に値のリストを渡すと、同一比較(アドレス比較)になる
- index で優先的に比較するよう関数を定めることができる(高速化)?
- debug は stderr に色々出力するだけで動作は変わらない?
実行する関数の代わりに戻り値を書ける
- (関数の場合は多値が返せるが)返せる値は1つ、2番めには情報が返る
注意点
- N+1 のような便利な記述はない
- 網羅性チェックもない
- 無限を表すテーブルを渡すとスタックオーバーフローする(たとえ最初の1つ2つしか調べなくても)
その他
他にもメンテナンスされていないライブラリがある
- luacombine(組み合わせ)
少しの修正で動く