Luaの文法

いちごパック > Luaの解説 > Luaの文法

ここではLuaの基本的な文法について説明します。 Windows環境へのインストールでつまづいている方は、 LuaとLuaRocksのWindowsインストールを参考にインストールしてみてください。

Luaの実行方法

文法の説明をする前に、Luaスクリプトの実行方法について説明します。 Luaはファイルをスクリプトとして実行することも、 1行ごとにコマンドを入力して即時実行することもできます。 なお、Luaで2つの命令を順に実行したい場合はスペースで区切って並べればよく、改行などは必要ありません。
Lua 5.3(lua53)がインストールされている場合、 スクリプト ichigo.lua を実行する場合は引数にスクリプト名を与えます。
PROMPT> lua53 ichigo.lua
一方、引数をつけずにlua53を起動すると、コマンド入力を受け付ける状態になりますので、 1行ごとにコマンドを入力して即時実行できます(インタラクティブモード)。
lua53の場合、コマンドラインオプションは次の通りです。他のバージョンでは多少変わるかもしれません。
-オプション解析を終了し、標準入力をLuaスクリプトファイルとして実行します。
--オプション解析を終了します。次の引数はスクリプト名として解釈されます。
-E環境変数を無視します。
-i1行ごとにコマンド入力を受け付けるインタラクティブモードで起動します。-vも指定したとみなされます。
-vLuaのバージョンを表示します。
-e commandcommandをLuaで実行します。
-l name起動時にプラグインを読み込みます。require('name')と同じです。
filenameオプションなしの引数filenameをLuaスクリプトファイルとして実行します。
スクリプト名をつけずにlua53を起動した場合は、-i -vと同じ動作をします。
インタラクティブモードでは、変数名のみを入力するとその中身が表示されます。
> a = 1
> a
1
> 
WindowsでLuaのインタラクティブモードを終了させたい場合は、 CTRL+Zキーを押してからENTERを入力してください。

コメント

文字列の内部以外では、--からその行の終わりまでがコメントとして扱われます。 また、--[[からはじまる場合、]]までがコメントとして扱われます。

変数と型

Luaの変数はどんな型でも保持できます(Javascript等と同じ)。 変数名を事前に宣言する必要はなく、=を使って変数に値を代入すると、 変数が存在しなければ新しく作成され、その変数は値に対応する型を持ちます。
内容
nilnilのみを保持します。データがないことを意味します。
booleantrue/falseのいずれかを保持します。
number(integer)整数を保持します。
number(float)浮動小数点数を保持します。
string文字列(8-bitデータの列)を保持します。
function関数を保持します。関数はLua、C言語のいずれでも書けます。
userdata(full)C言語で使うメモリブロックです。Luaから中身に直接アクセスすることはできません。C言語側でmetatablesを使うことで、中身へのアクセス機能を提供できます。
userdata(light)C言語で使うメモリポインタです。Luaから中身に直接アクセスすることはできません。
threadLua実行中の実行状態そのものを保持します。threadという名前ですが、OSのスレッドとは関係ありません。コルーチンの実装に利用できます。
table連想配列(key-valueタイプの配列)を保持します。keyとvalueはnil以外であればどの変数でもかまいません。また、keyがない場合の値を読み出すとnilになります。keyがfloat型を持つ整数(例:15.0)の場合は、対応する整数(例:15)に変換された形で扱われます。配列や構造体を使いたいなら、Luaではtableを使う必要があります。
function、userdata(full)、thread、tableは、実データへの参照として扱われます。 これらの変数を代入してもそのコピーは作られず、同じ実データへの参照がコピーされます。
変数の名前として使えない、Luaの予約語は次の通りです。
andornot
forindobreakend
whilerepeatuntil
ifthenelseifelse
goto
niltruefalse
functionlocalreturn
Luaは大文字小文字を区別しますので、例えばandは予約語ですがAndは予約語ではありません。
関数type(データ)を呼び出すと、データの型を文字列として返します。 インタラクティブモードの例を以下に示します。
> a = 1
> b = type(a)
> a
1
> b
number
> type(b)
string
>
なお、次の例のように、存在しない変数(この例ではf)はnilとして扱われます。
> f
nil
> type(f)
nil
>

Luaの演算

Luaでは、次の演算が定義されています。 演算関数は、演算子が対応していない型に対して使われた場合に呼び出す関数の名前です。演算関数については後述します。
演算子演算関数内容
-a__unm(a)符号反転(numberのみ)
a+b__add(a,b)加算(numberのみ)
a-b__sub(a,b)減算(numberのみ)
a*b__mul(a,b)乗算(numberのみ)
a/b__div(a,b)浮動小数点数の除算(numberのみ)
a//b__idiv(a,b)整数の除算(numberのみ)
a%b__mod(a,b)剰余演算(numberのみ)
a^b__pow(a,b)aのb乗(numberのみ)
a&b__band(a,b)aとbのビット単位AND(整数のみ)
a|b__bor(a,b)aとbのビット単位OR(整数のみ)
a~b__bxor(a,b)aとbのビット単位XOR(整数のみ)
~a__bnot(a)aのビット単位NOT(整数のみ)
a<__shl(a,b)aをbビット左シフト(整数のみ)
a>>b__shr(a,b)aをbビット右シフト(整数のみ)
a..b__concat(a,b)aとbを文字列として結合(stringかnumberのみ)
#a__len(a)文字列の長さ(stringのみ)
a==b__eq(a,b)aとbが等しいかをboolで返す(tableとfull userdataで内容が違う場合のみ__eqを呼び出す)
a~=ba==bの結果の否定を返す
a__lt(a,b)aがbより小さいかをboolで返す(stringかnumberのみ)
a<=b__le(a,b)aがb以下であるかをboolで返す(stringかnumberのみ)
a>bb>aを返す
a>=bb<=aを返す
a[b]__index(a,b)bをkeyとしたaのvalueを返す(aがtable以外か、tableにbが存在しなければ__indexを呼び出す)__indexに関数ではなくtable変数をセットした場合、a.__index[b]を呼ぶ
a[b] = value__newindex(a,b,c)bをkeyとしたaのvalueを設定する(aがtable以外か、tableにbが存在しなければ__newindexを呼び出す)__indexに関数ではなくtable変数をセットした場合、a.__newindex[b]を呼ぶ
a(varargs)__call(varargs)任意個の引数varargを引数として関数aを呼び出す(functionのみ)
(変数aの消滅時)__gc(a)変数aが消滅する際に呼び出される(tableまたはuserdata(full)のみ、__gcとしてfunctionを設定する必要がある)
(文字列変換時)__tostring(a)aを文字列に変換して返す

数値(number)

0-9で書かれた10進数、または0x(0X)からはじまる16進数は数値として扱われます。 小数点(.)、10進数の数値の後に置かれた10進数の指数表示(e、E)、 または16進数の数値の後に置かれた2進数の指数表示(p,P)がある場合は浮動小数点数(float)として、 そうでない場合は整数(integer)として扱われます。
> 10
10
> 0x10
16
> 10.0
10.0
> 10e1
100.0
> 0x10p1
32.0
>

文字列(string)

シングルクウォート(')、ダブルクウォート(")で括った部分は文字列として扱われます。 どちらのクウォート文字を用いた場合でも、次のエスケープシーケンスが利用できます。
\aベル文字
\bBS(バックスペース)
\rCR(キャリッジリターン)
\nLF(ラインフィード)
\fFF(フォームフィード)
\t水平タブ文字
\v垂直タブ文字
\\1文字分の\
\'1文字分の'
\"1文字分の"
\(改行)改行
\xHH2文字の16進数コードHHで指定された文字
\DDD3文字の10進数コードDDDで指定された文字
\u{HHH}{}で囲まれた可変長の16進数コードで指定されたユニコード文字をUTF-8に変換した文字列
\z直後にある空白・改行文字を、それ以外の文字が見つかるまで無視
また、[[と]]で囲まれた部分も文字列として扱われます。 [[の間に=を1つ以上入れ、]]の間にも同じ数の=を入れることで、[[自身を文字列に含めることもできます。 [[の直後に改行がある場合に限り、その改行は無視されます。
インタラクティブモードで文字列を使う例を示します。
> a = 'ichigo'
> a
ichigo
> a = [[ichigo15]]
> a
ichigo15
> a = [[
>> 15ichigo]]
> a
15ichigo
> a = [[ichigo
>> 15]]
> a
ichigo
15
>
文字列の結合は..で行えます。 数値の場合は、結合時に文字列に変換されます。
> a = 'ichigo'
> b = 15
> type(a)
string
> type(b)
number
> a..b
ichigo15
数値以外の変数を文字列に結合しようとすると、エラーになります。
> a = 'ichigo'
> b = true
> a..b
stdin:1: attempt to concatenate a boolean value (global 'b')
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
>

複数変数の一括代入

Luaでは、カンマ(,)で並べて一度に複数変数への代入ができます。
> a = 1
> b = 2
> c = 'ichigo'
> d,e = a..b,c
> type(d)
string
> d
12
> e
ichigo
>

連想配列(table)

{}で括ると連想配列になります。 連想配列も数値や文字列と同様に、変数に代入して利用できます。 tableへの変数は参照として扱われます。tableを代入してもtableのコピーは作られず、同じtableへの参照がコピーされます。
{}の中にはカンマ(,)で区切った複数のvalue、または[key]=valueを置くことができます。 [key]=valueをkey=valueと書いた場合、keyは文字列(string)として扱われます。 valueのみを書いた場合は、先頭から順に1=、2=、のように1から始まるkeyが割り当てられます。 1つの{}に[key]=valueとvalueを混ぜて書いた場合は、 [key]=valueでない部分について、1から始まるkeyが割り当てられます。
{}の中に配置されたデータの代入順序は決まっていませんので、 代入時に関数呼び出しを行う場合は、順序に依存しないようにご注意ください。
連想配列は、a[b]の形、あるいはa.bの形でアクセスできます。a.bはa["b"]として扱われます。
インタラクティブモードで連想配列を使う例を示します。
> a = { 15, ichigo='ichigo', [15]='ichigo15' }
> type(a)
table
> a[1]
15
> a.ichigo
ichigo
> a["ichigo"]
ichigo
> a[15]
ichigo15
>

関数

関数も数値や文字列と同様に、変数に代入して利用できます。 関数への変数は参照として扱われます。関数を代入しても関数のコピーは作られず、同じ関数への参照がコピーされます。
関数は次のいずれかの形で定義できます。 ブロックと書かれた部分には関数の中身を与えます。
種類定義方法
無名関数function (パラメータリスト) ブロック end
名前つき関数function name (パラメータリスト) ブロック end
local用の名前つき関数local function name (パラメータリスト) ブロック end
関数は名前つき、名前なしの両方の形で定義できますが、 名前つき関数として定義すると、名前つき関数が生成された後で、 指定された関数名を名前とするfunction型の変数にその関数が代入されます。
関数を呼び出す場合は、関数名(パラメータリスト)とします。 パラメータリストはカンマ(,)で区切った、0個以上の変数列です。 関数の呼び出し時には、余分な引数は無視され、足りない引数はnilで埋められます。
関数の中で値を返す場合は、return 値とします。 代入と同様、カンマで区切ることで複数の値を返せます。
インタラクティブモードで関数を使う例を示します。
> a = function (v) return v+1 end
> a(1)
2
> function a(v) return v..15 end
> a('ichigo')
ichigo15
>
また、オブジェクト指向でコードを書きやすくするために、 コロン(:)による関数呼び出し機能が用意されています。 この機能を使うと、要素に関数を持つ連想配列をオブジェクトのように利用できます。
コロンはa:b(c)はa.b(a,c)として扱います。 直後の変数を直前の変数のkeyとしたvalueを関数として、 第1引数にその直前の変数を渡して呼び出します。
> a = {}
> function a.b() return a.c end
> a.c = 2
> a.b()
2
>

条件分岐とループ制御

Luaでは条件分岐やループを以下の方法で書けます。 ifやwhile等で使う条件判定は、falseかnilの場合のみ偽になり、それ以外の値は0なども含めて真として扱われます。
local直後の変数名をブロック内のみ有効な変数(ローカル変数)として扱います。
do ブロック endブロック内に書かれたコマンドを上から順に実行します。do〜endまでが1つの文として扱われます。
if 条件 then ブロック elseif 条件 then ブロック else ブロック end条件を満たすブロックを実行します。elseif節とelse節は省略可能です。
while 条件 do ブロック end条件を満たす限りブロックを繰り返します。条件はブロックの前に評価されます。
repeat ブロック until 条件条件を満たすようになるまでブロックを繰り返します。条件はブロックの後で評価されます。
for 変数名=初期値,終了値,ステップ値 do ブロック end(ステップ値は省略可能)初期値から開始し、終了値になるまで、ブロックを実行してステップ値を加えます。forで与える3つの数値は最初に一度だけ実行され、numberに変換されます。
for 変数リスト in 関数と2つの引数 do ブロック end関数(2つの引数)を実行して変数リストに代入した結果がnilになるまで、ブロックを繰り返し実行する。
繰り返しになりますが、Luaでは条件分岐の条件としてfalseは偽でも0は真と解釈されることにご注意ください。

do〜endの例

do〜endの間に置かれたコードはインタラクティブモードのコードとしては扱われませんので、 中身を表示するためにprint()を使っています。
> a = 1
> do
>>   local a = 2
>>   print(a)
>> end
2
> a
1
>

条件分岐の例

> function test(v) if v > 1 then print('ichigo') else print('15') end end
> test(1)
15
> test(3)
ichigo
>

whileループとrepeatループの例

whileループは条件を満たす間、repeatループは条件を満たさない間、ブロックを実行します。
> a = 1
> while a < 5 do print(a) a = a + 1 end
1
2
3
4
> a = 1
> repeat print(a) a = a + 1 until a >= 5
1
2
3
4
>

forループの例

> for a=1,5,2 do print(a) end
1
3
5
> for a=1,4,2 do print(a) end
1
3
> for a=4,1,-2 do print(a) end
4
2
>

for〜inループの例

for〜inループでは、はじめに関数、状態、変数を与え、 関数の戻り値がnilにならない限り、変数を戻り値に更新してブロックを呼び出します。
> function f(s,v) if v < s.vend then return v + 1 else return nil end end
> stat = {}
> stat.vend = 5
> for v in f,stat,0 do print(v) end
1
2
3
4
5
>
関数fはvがs.vend(この例では5)より小さければv+1を、そうでなければnilを返す関数です。 vの初期値を0としてfor〜inを呼び出していますので、vが5になるまで、 vが1ずつ増やされた後でprint(v)が呼び出されています。

メタテーブル(metatable)

Luaでは、metatableという機能を使って、未定義の演算を定義できます。 metatableとはLuaのtable型変数で、__addや__sub等をkeyとした演算関数を持つtableです。
ほとんどの型については、システム全体で1つのmetatableを持ちます。 ただし、tableとuserdata(full)については、1つずつ個別のmetatableを持つことができます。
Luaからは、tableのmetatableのみ変更できます。table以外の型に対するmetatableを変更したい場合は、C言語から変更する必要があります。 tableに対するmetatableを変更したい場合は、metatableとして使うtableを作成したうえで、 システム関数setmetatable(table,metatable)を呼び出します。
> objmeta = {}
> function objnew(v) local obj = {value = v} setmetatable(obj,objmeta) return obj end
> function objmeta.__add(a,b) local obj = objnew(0) obj.value = a.value + b.value return obj end
> a = objnew(1)
> b = objnew(2)
> a.value
1
> b.value
2
> c = a + b
> c.value
3
>

Luaでオブジェクト指向

Luaのtableとmetatableを使うと、オブジェクト指向でコードを書きやすくなります。 これらを使ってコードを書きたい場合は、次のように書くと良いでしょう。
tableオブジェクト
new関数オブジェクトを確保し、メソッドとmetatableをオブジェクトに設定して返す関数(オブジェクト確保の際に使用)
メソッドtableにkeyをメソッド名、valueをメソッド実装関数とした変数を追加(呼び出す際はコロンを使用)
独自の演算子metatableにkeyを演算関数名、valueを演算実装関数とした変数を追加(呼び出す際は演算子を使用)
2次元のベクトルを実装したvector2オブジェクトの例を示します。 dotは2つのベクトルの内積、lenはベクトルの長さを求めるメソッドです。
> vector2meta = {}
> function vector2meta.__add(v1,v2) local obj=vector2new() obj.x=v1.x+v2.x obj.y=v1.y+v2.y return obj end
> function vector2meta.__sub(v1,v2) local obj=vector2new() obj.x=v1.x-v2.x obj.y=v1.y-v2.y return obj end
> function vector2meta.__tostring(v1) return '('..v1.x..','..v1.y..')' end
> function vector2dot(v1,v2) return v1.x*v2.x+v1.y*v2.y end
> function vector2len(v1) return math.sqrt(v1.x*v1.x+v1.y*v1.y) end
> function vector2new(x_,y_) local obj={x=x_,y=y_,dot=vector2dot,len=vector2len} setmetatable(obj,vector2meta) return obj end
>
> a = vector2new(1,0)
> b = vector2new(2,2)
> a
(1,0)
> b
(2,2)
> a:dot(b)
2
> c = a + b
> c
(3,2)
> c:len()
3.605551275464
> c = a - b
> c
(-1,-2)
> c:len()
2.2360679774998
>