3.4 式
Lua の基本式 (basic expression) は次の通りです:
exp ::= prefixexp
exp ::= nil | false | true
exp ::= Numeral
exp ::= LiteralString
exp ::= functiondef
exp ::= tableconstructor
exp ::= ‘...’
exp ::= exp binop exp
exp ::= unop exp
prefixexp ::= var | functioncall | ‘(’ exp ‘)’
数値と文字列リテラルは § 3.1 で、変数は § 3.2 で、関数定義は § 3.4.11 で、関数呼び出しは § 3.4.10 で、テーブルの構文は § 3.4.9 でそれぞれ説明されます。三つのドット (...
) で表される可変長引数式は可変長引数を取る関数の直下でのみ利用できます。詳しくは § 3.4.11 で説明されます。
二項演算子には算術演算子 (§ 3.4.1)・ビット演算子 (§ 3.4.2)・関係演算子 (§ 3.4.4)・論理演算子 (§ 3.4.5)・連結演算子 (§ 3.4.6) があり、単項演算子には単項マイナス (§ 3.4.1)・ビットごとの単項 NOT (§ 3.4.2)・単項論理否定演算子 (§ 3.4.5)・単項長さ演算子 (§ 3.4.7) があります。
関数呼び出しと可変長引数式はどちらも複数の値を返す可能性があります。関数呼び出しが文として使われる (§ 3.3.6) ときは返り値のリストがゼロ要素に調整され、返り値は全て無視されます。リストの最後の (あるいは唯一の) 要素が複数の値を返す式の場合には、その式には調整が行われません (ただし式が括弧で囲まれているなら調整されます)。これ以外の全ての文脈において、Lua は返り値のリストを一つの要素に調整します。この調節は最初の要素を取って他の要素を捨てる、あるいは値が存在しないときには nil を足すことで行われます。
関数呼び出しの例を示します:
f() -- 返り値はゼロ要素に調整される
g(f(), x) -- f() の返り値は一要素に調整される
g(x, f()) -- g は x と f() の全ての返り値を受け取る
a,b,c = f(), x -- f() の返り値は一要素に調整される (c は nil を受け取る)
a,b = ... -- a は可変長引数の最初の要素を受け取る
-- b は二番目の要素を受け取る (可変長引数が無い場合には
-- 両方とも nil となる)
a,b,c = x, f() -- f() の返り値は二要素に調整される
a,b,c = f() -- f() の返り値は三要素に調整される
return f() -- f() の返り値を全て返す
return ... -- 受け取った可変長引数を全て返す
return x,y,f() -- x, y, f() の全ての返り値を返す
{f()} -- f() の全ての返り値からなるリストを作る
{...} -- 全ての可変長引数からなるリストを作る
{f(), nil} -- f() は一要素に調整される
括弧で囲われた式は必ず一つの値を返します。例えば (f(x,y,z))
が表すのは f
が複数の値を返したとしても一つの値であり、具体的には f
の返り値の最初の要素、値が返らない場合には nil です。
3.4.1. 算術演算
Lua は次の算術演算をサポートします:
+
(加算)-
(減算)*
(乗算)/
(浮動小数除算)//
(切り捨て除算)%
(モジュロ演算)^
(べき乗演算)-
(単項マイナス演算)
浮動小数除算とべき乗以外の演算は次のように処理されます: オペランドが両方とも整数なら、整数に対する演算結果の整数が返り値です。それ以外の場合でオペランドが両方とも数値なら、それらを浮動小数点数に変換してからマシンの規則 (通常は IEEE 754 規格) に従った浮動小数点数演算を行い、その結果を返り値とします。また文字列ライブラリは算術演算において文字列を数値に強制変換します。詳細は § 3.4.3 を参照してください。
べき乗 (^
) と浮動小数除算 (/
) は必ずオペランドを浮動小数点数に変換してから計算され、その結果は浮動小数点数です。べき乗は ISO C 関数 pow
を使うので、整数でない指数にも対応します。
切り捨て除算 (floor division, //
) は商を負の無限大に向かって切り捨てる除算です。これは同じオペランドで除算を計算してから床関数を適用することに相当します。
モジュロ演算は商を負の無限大に向かって切り捨てる除算 (切り捨て除算) の余りとして定義されます。
整数算術演算でオーバーフローが起こった場合には、全ての演算においてラップアラウンドが起こります。
3.4.2. ビットごとの演算
Lua は次のビットごとの演算をサポートします:
&
(ビットごとの AND)|
(ビットごとの OR)~
(ビットごとの排他 OR)>>
(右シフト)<<
(左シフト)~
(ビットごとの単項 NOT)
ビットごとの演算は全てオペランドを整数に変換 (§ 3.4.3) してから計算され、結果も整数です。
右および左シフトは空いたビットをゼロで埋めます。負のシフトは逆方向へのシフトとなり、整数のビット幅以上のシフトはゼロとなります (シフトで全てのビットが消えるため)。
3.4.3. 変換と強制変換
Lua は一部の型や表現の間の自動的な変換 (conversion) を実行時に行います。例えばビットごとの演算は必ず浮動小数点のオペランドを整数に変換し、べき乗と切り捨て除算は必ず整数のオペランドを浮動小数点数に変換します。その他の全ての算術演算では、整数と浮動小数点が混ざったオペランドが与えられると整数が浮動小数点数に変換されます。また C API は必要に応じて整数と浮動小数点数の間で双方向に変換を行います。さらに文字列の連結は引数として文字列だけではなく数値も受け付けます。
整数から浮動小数点数への変換では、整数が浮動小数点数で正確に表せるならそれが結果となります。そうでなければ整数より大きくて一番近い値あるいは整数より小さくて一番近い値のどちらかが結果となります。この変換は失敗しません。
浮動小数点数から整数の変換では、まず浮動小数点数が正確な整数表現を持つか (浮動小数点数の整数部分が整数で表せる範囲に収まるか) が調べられます。もし整数表現を持つならそれが結果であり、持たないなら変換は失敗します。
Lua は必要な場合には文字列から数値への強制変換 (coercion) も行います1。具体的には文字列ライブラリが全ての算術演算において文字列を数値に強制変換するメタメソッドを設定します。変換が失敗した場合には文字列ライブラリがもう一方のオペランドのメタメソッドの呼び出しを (存在するなら) 試み、それが不可能ならエラーを送出します。なおビットごとの演算ではこの強制変換は起こりません。
とは言え、こういった暗黙の強制変換に頼らないのが良い習慣と言えます。常に起こるわけではないからです: "1" == 1
は false ですが、"1" < 1
はエラーを送出します (§ 3.4.4)。こういった変換は主に互換性のために存在しているので、将来のバージョンでは削除される可能性があります。
文字列から整数あるいは浮動小数点数への変換では数値の構文規則と Lua 字句解析器の規則が使われます。文字列は前後に空白、先頭に符号を含むことができ、文字列から数値への変換では、小数点としてドットだけではなく現在のロケールの小数点も利用できます (これに対して Lua の字句解析器が受け付けるのはドットだけです)。文字列が正当な数値でない場合には強制変換が失敗します。必要な場合には、この一つ目のステップの後に整数と浮動小数点数の間の前述した規則による変換がさらに起こります。
数値を文字列に変換すると人間が読めるフォーマットの文字列が手に入りますが、そのフォーマットは特に規定されていません。フォーマットを指定して数値を文字列に変換するには string.format
を使ってください。
3.4.4. 関係演算子
Lua は次の関係演算子をサポートします:
==
(等号演算)~=
(等号の否定演算)<
(小なり演算)>
(大なり演算)<=
(小なり等号演算)>=
(大なり等号演算)
こういった関係演算子の演算結果は false または true です。
等号演算 (==
) はまずオペランドの型を調べます。型が違うなら結果は false であり、そうでなければオペランドの値が比較されます。文字列は要素がバイト列として同じとき等しくなり、数値は表される数学的な値が等しいとき等しくなります。
テーブル・ユーザーデータ・スレッドは参照によって比較されます。つまり二つのオブジェクトが等しいのは同じオブジェクトを参照しているときだけであり、新しく作られたオブジェクト (テーブル・ユーザーデータ・スレッド) は既存のオブジェクトのどれとも異なります。関数はそれ自身と常に等しく、検出可能な違い (異なる動作あるいは定義) を持つ二つの関数は常に異なります。異なるタイミングで作られた検出可能な違いを持たない二つの関数は、等しいこともあれば異なることもあります (内部のキャッシュ処理に依存します)。
Lua がテーブルとユーザーデータを比較する方法は __eq
メタメソッドで変更できます (§ 2.4)。
等価比較では文字列と数値の間の変換は行われません。例えば "0" == 0
は false であり、t[0]
と t["0"]
は異なるテーブルエントリーを表します。
~=
演算子は等号演算 (==
) の否定です。
順序演算子は次のように動作します。まず両方の引数が数値なら、部分型に関わらず数学的な値として比較されます。次に両方の引数が文字列なら、現在のロケールに照らして比較されます。それ以外の場合にはメタメソッド __lt
または __le
が試されます (§ 2.4)。比較 a > b
は b < a
に変換され、a >= b
は b <= a
に変換されます。
IEEE 754 に従って、特別な値 NaN は NaN を含む任意の値より小さくなく、等しくなく、大きくないとされます。
3.4.5. 論理演算子
Lua は論理演算子 and, or, not を持ちます。制御構造 (§ 3.3.4) と同様に、全ての論理演算子は false と nil を偽と扱い、それ以外の全ての値を真と扱います。
否定演算子 not は常に false または true を返します。連言演算子 and は第一引数が false または nil ならそれを返し、そうでなければ第二引数を返します。選言演算子 or は第一引数が false と nil 以外ならそれを返し、そうでなければ第二引数を返します。and と or はどちらも短絡評価を行うので、 第二引数は必要な場合にのみ評価されます。論理演算子の例を示します:
10 or 20 --> 10
10 or error() --> 10
nil or "a" --> "a"
nil and 10 --> nil
false and error() --> false
false and nil --> false
false or nil --> nil
10 and 20 --> 20
3.4.6. 連結演算子
Lua の文字列演算子は二つのドット ..
で表されます。両方のオペランドが文字列あるいは数値であれば、数値がフォーマットの指定されない文字列に変換されます (§ 3.4.3)。そうでなければ __concat
メタメソッドが呼び出されます (§ 2.4)。
3.4.7. 長さ演算子
長さ演算子は単項の前置演算子 #
で表されます。
文字列の長さはバイト数です (全ての文字が一バイトであれば、この値は普通の意味での「文字列の長さ」に等しくなります)。
長さ演算子をテーブルに適用すると、テーブルの境界 (border) が返ります。テーブルの境界とは次の条件を満たす任意の自然数です:
(border == 0 or t[border] ~= nil) and t[border + 1] == nil
言い換えると、テーブル境界とは空要素が後ろにある要素の添え字です (添え字 1 が空なら 0 が境界になります)。
ちょうど一つの境界を持つテーブルを列 (sequence) と呼びます。例えばテーブル {10, 20, 30, 40, 50}
の境界は 5 だけなので列です。またテーブル {10, 20, 30, nil, 50}
の境界は 3 と 5 の二つなので、これは列ではありません (第四要素の nil を穴 (hole) と呼びます)。テーブル {nil, 20, 30, nil, nil, 60, nil}
は三つの境界 (0, 3, 6) と三つの穴 (第一・第二・第五要素) を持つので、これも列ではありません。またテーブル {}
は境界 0 を持ちます。自然数でないキーに何が入っていてもテーブルが列であるかどうかには全く影響しないことに注意してください。
t
が列のとき、#t
は t
が持つ唯一の境界を返します。この値は「列の長さ」という言葉が表す自然な値と一致します。t
が列でない場合には、#t
は複数ある境界のうち任意のものを返します (実際に何が返るかはテーブルの内部表現、つまりテーブルの要素数や自然数でないキーのメモリアドレスに依存します)。
テーブルの長さの計算は最悪でも O(log n) 時間で実行できることが保証されています (n はテーブルが持つ自然数のキーの最大値)。
文字列以外の任意の値に対する長さ演算子の振る舞いはプログラムから __len
メタメソッドを設定することで変更できます (§ 2.4)。
3.4.8. 演算子の優先順位
Lua の演算子の優先順位を次の表に示します。下にある演算子ほど高い優先度を持ちます。
or
and
< > <= >= ~= ==
|
~
&
<< >>
..
+ -
* / // %
単項演算子 (not # - ~)
^
普通のプログラミング言語と同じように、括弧を使って式の優先順位を変えられます。連結 (..
) とべき乗 (^
) は右結合であり、その他の二項演算子は全て左結合です。
3.4.9. テーブルコンストラクタ
テーブルコンストラクタはテーブルを作成する式です。テーブルコンストラクタが評価されるたびに新しいテーブルが作成され、そのときには空のテーブルを作ることも、いくつかのフィールドを初期化することもできます。テーブルコンストラクタの一般的な構文を示します:
tableconstructor ::= ‘{’ [fieldlist] ‘}’
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
fieldsep ::= ‘,’ | ‘;’
[exp1] = exp2
という形をしたフィールドはキーが exp1
でバリューが exp2
のエントリーを新しくできるテーブルに追加します。name = exp
という形をしたフィールドは ["name"] = exp
と等価であり、exp
という形をしたフィールドは [i] = exp
と等価となります。後者において i
はこの形をしたフィールドの数であり、他の形のフィールドは自動的に挿入される添え字に影響しません。例えば
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
というコードは次のコードと同じです:
do
local t = {}
t[f(1)] = g
t[1] = "x" -- 一つ目の式
t[2] = "y" -- 二つ目の式
t.x = 1 -- t["x"] = 1
t[3] = f(x) -- 三つ目の式
t[30] = 23
t[4] = 45 -- 四つ目の式
a = t
end
テーブルコンストラクタにおける代入の順序は定義されません (順序が意味を持つのは同じキーが複数回表れるときだけです)。
リストに含まれる最後のフィールドが exp
という形をしており、その式が関数呼び出しまたは可変長引数式である場合には、その式が返す全ての値がリストへ連続して入れられます (§ 3.4.10)。
フィールドのリストは最後に分離記号を持つことができます。機械で生成されたコードで便利なようにこうなっています。
3.4.10. 関数呼び出し
Lua の関数呼び出しは次の構文を持ちます:
functioncall ::= prefixexp args
関数呼び出しでは prefixexp
と args
が最初に評価され、prefixexp
の値が関数型を持っているならその関数が引数と共に呼ばれます。そうでない場合には prefixexp
の __call
メタメソッドが (もしあるなら) 呼ばれます。メタメソッドが呼ばれるときの第一引数は prefixexp
の値であり、その後に元の呼び出しに渡された引数が続きます (§ 2.4)。
オブジェクトのメソッド呼び出しを真似た次の記法が用意されています:
functioncall ::= prefixexp ‘:’ Name args
v:name(args)
は v.name(v,args)
の糖衣構文です (ただし v
は一度しか評価されません)。
引数は次の構文を持ちます:
args ::= ‘(’ [explist] ‘)’
args ::= tableconstructor
args ::= LiteralString
引数の式は全て呼び出される前に評価されます。f{fields}
は f({fields})
の糖衣構文であり、一つの新しいテーブルが引数となります。また f'string'
, f"string"
, f[[string]]
はどれも f('string')
の糖衣構文であり、一つの文字列が引数となります。
クローズ予約変数を持たないブロックにおける return functioncall
という形の呼び出しを末尾呼び出し (tail call) と呼びます。Lua は末尾呼び出し最適化 (proper tail call) と末尾再帰最適化 (proper tail recursion) を実装しているので、ネストできる末尾呼び出しの数に制限はありません。ただし末尾呼び出しを使うとデバッグ情報が削除されます。末尾呼び出しとみなされる条件は決まっており、return
が単一の関数呼び出しを引数としていて、かつそのブロックにクローズ予約変数が含まれないときだけです。この条件により呼ぶ側の関数が行う最後の処理が関数呼び出しであることが保証されます。例えば次の例はどれも末尾呼び出しではありません:
return (f(x)) -- 返り値が一要素に調整される
return 2 * f(x) -- 返り値が一要素に調整されるに 2 が掛けられる
return x, f(x) -- 関数呼び出し以外の返り値がある
f(x); return -- 返り値が無視される
return x or f(x) -- 返り値が一要素に調整される
3.4.11. 関数定義
関数定義の構文は次の通りです:
functiondef ::= function funcbody
funcbody ::= ‘(’ [parlist] ‘)’ block end
次の糖衣構文を使うと関数を簡単に定義できます:
stat ::= function funcname funcbody
stat ::= local function Name funcbody
funcname ::= Name {‘.’ Name} [‘:’ Name]
文
function f () body end
は次のように変換されます:
f = function () body end
また文
function t.a.b.c.f () body end
は次のように変換されます:
t.a.b.c.f = function () body end
同様に文
local function f () body end
は次のように変換されます:
local f; f = function () body end
これは次とは異なります:
local f = function () body end
(二つの変換の意味が異なるのは、関数の本体が f
への参照を含むときだけです)
関数定義は実行可能な式であり、その値は関数型を持ちます。Lua がチャンクを事前コンパイルするときには含まれる関数も事前コンパイルされますが、作成はされません。関数は Lua が関数定義を実行するタイミングでインスタンス化 (instantiation) されます (関数をインスタンス化することをクローズ (close) するとも言います)。こうして得られる関数のインスタンスが関数定義式の値であり、クロージャ (closure) と呼ばれます。
関数のパラメータは引数の値で初期化されたローカル変数として扱われます:
parlist ::= namelist [‘,’ ‘...’] | ‘...’
Lua が関数を呼ぶとき、引数のリストはパラメータのリストと長さが一致するよう調整されます。ただしパラメータリストが三つのドット ...
で終わる可変長引数関数 (vararg function) の場合には調整は行われません。可変長引数関数は引数リストを調整する代わりに余計な引数をまとめて可変長引数式 (vararg expression) として関数に渡します (この式も三つのドットで表されます)。複数の返り値を持つ関数と同様、この可変長引数式の値は余計な引数のリストです。可変長引数式が他の式の一部あるいは式リストの最後でない要素で使われた場合には、一要素に調整された値が返されます。一方で式リストの最後の要素に可変長引数式が使われた場合には、調整は行われません (最後の要素であっても括弧で囲われていれば調整されます)。
例として次の関数定義を考えます
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
このとき引数からパラメータと可変長引数式へのマッピングは次のようになります:
呼び出し パラメータ
---------------------------
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, ... --> (空リスト)
g(3, 4) a=3, b=4, ... --> (空リスト)
g(3, 4, 5, 8) a=3, b=4, ... --> 5 8
g(5, r()) a=5, b=1, ... --> 2 3
関数は return 文で値を返します (§ 3.3.4)。実行が return 無しに関数の最後まで到達した場合には、関数の返り値は存在しないものとされます。
関数が返せる値の数にはシステムに依存する上限があります。この上限は 1000 より大きいことが保証されています。
コロンを使った構文を使うとオブジェクト指向プログラミングにおけるメソッドを真似ることができます。この場合には追加のパラメータ self
が関数に追加されます。例えば文
function t.a.b.c:f (params) body end
は次の文を表す糖衣構文です:
t.a.b.c.f = function (self, params) body end
-
訳注: ここで「強制変換 (coercion)」という言葉は「変換できない可能性が十分あるので実装されないことも選択肢としてあり得るような変換」ぐらいの意味で使われている。プログラミングでは明示的な型変換を conversion と呼び、暗黙な型変換を coercion と呼ぶことが多いが、ここではそうでない。[return]