M.Hiroi's Home Page
http://www.geocities.jp/m_hiroi/

Lua Programming

お気楽 Lua プログラミング超入門

[ PrevPage | L u a | NextPage ]

Lua の概要

Lua (ルア) は、アプリケーションに組み込むことを目的とした軽量のスクリプト言語です。Lua - Wikipedia によると、『Lua は、リオデジャネイロ・カトリカ大学の情報工学科コンピュータグラフィックステクノロジーグループ TeCGraf によって設計開発されたスクリプト言語』 とのことです。

Lua は JavaScript とよく似ています。見た目は JavaScript と異なりますが、クロージャなどの関数型言語の機能を取り込んでいるところや、プロトタイプベースのオブジェクト指向が可能なところは JavaScript にそっくりです。このほかにも、コルーチン (coroutine) といった便利な機能が備わっていて、これらを組み合わせることで高度で柔軟なプログラミングが可能になっています。

そして M.Hiroi が驚いたのはその実行速度です。Lua はプログラムをバイトコードにコンパイルする方式です。ネイティブコードにコンパイルする処理系にはかないませんが、バイトコードにコンパイルする方式ではかなり速いほうだと思います。実際に拙作のページ Algorithms with Python 再帰定義 の「たらいまわし関数」で実行速度を比較してみました。

リスト 1 : たらいまわし関数 (Lua)

function tak(x, y, z)
  if x <= y then
    return z
  else
    return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y))
  end
end

それでは実行結果を示します。tak(18, 9, 0) を計算しました。比較のため Python, Ruby, Gauche, CLISP の実行結果を示します。これらの処理系はプログラムをバイトコードにコンパイルするものです。

表 : tak(18, 9, 0) の結果
処理系
Ruby (ver 1.9.2)8.55
Python (ver 2.7)6.92
Lua (ver 5.1.4)3.94
Gauche (ver 0.8.12)3.16
CLISP (ver 2.44)2.57
Gauche (ver 0.9.1)1.88

CLISP (Common Lisp) や Gauche (Scheme) の最新バージョンにはかないませんが、Ruby や Python よりもずいぶんと高速です。Lua には JIT を搭載した LuaJIT の開発が行われていて、LuaJIT を使うと実行速度は劇的に速くなるようです。興味のある方は、ほかのプログラムでも試してみてください。

●Lua の対話モード

ところで、Lua の基本を学ぶのであれば、Lisp / Scheme の REPL (read eval print loop) のような対話モードがあると便利です。Python にも対話モードがありますし、Ruby には irb (interactive ruby) というツールがあります。Lua の場合、プログラムを実行するコマンド lua をコマンドプロンプトで起動すると、次のようなプロンプトが表示されて入力待ちになります。

C>lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
>

これでプログラムを入力することができますが、REPL のように変数名を入力しても値を表示することはできません。

> a = 1 + 1
> print(a)
2
> a
>>

このように変数名 a を入力しただけでは値を表示することはできません。REPL のように使いたい場合は、コマンド ilua を使います。

C>ilua

C>lua.exe -lilua
ILUA: Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
"quit" to end
>

ilua は lua にオプション -lilua を指定して起動するだけのコマンドです。ilua では次のように変数名を入力するだけで、その値を表示することができます。

> a = 1 + 1
> a
2
>

これで REPL のようにプログラムを入力して実行することができます。

終了する場合は、Windows であれば CTRL-Z, UNIX 系の OS であれば CTRL-D を入力してください。ただし Windows の場合、一度入力しただけでは終了しません。ilua モードから lua モードに切り替わるだけなので、もう一度 CTRL-Z を入力してください。

●FizzBuzz 問題

それでは簡単な例題として FizzBuzz 問題を Lua で解いてみましょう。FizzBuzz 問題は 1 から 100 までの値を表示するとき、3 の倍数のときは Fizz を、5 の倍数ときは Buzz を表示するというものです。FizzBuzz 問題の詳細については Fizz Buzz - Wikipedia をお読みください。

プログラムは次のようになります。

リスト 2 : FizzBuzz 問題 (fizzbuzz.lua)

function fizzbuzz()
  for i = 1, 100 do
    if i % 15 == 0 then
      io.write("FizzBuzz")
    elseif i % 3 == 0 then
      io.write("Fizz")
    elseif i % 5 == 0 then
      io.write("Buzz")
    else
      io.write(i)
    end
    io.write(" ")
  end
end

fizzbuzz()

Lua の場合、関数を定義するときは function を使います。そのあとに関数名と引数を指定します。C言語と違って Lua の変数に型はありません。どんなデータ型でも格納することができます。

実行結果は次のようになります。

C>lua fizzbuzz.lua
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 
22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 
Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 
61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz 
Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz

Lua の基礎知識 (データ構造編)

Lua はC言語や Perl と同じく、手続き型のプログラミング言語です。Lua も Perl と同様に、プログラムの実行を制御する「文」(命令文ともいう)、データを格納する「変数」、決められた処理を行う「関数」、という構造に分かれます。C言語や Perl のように、文の終わりにセミコロン ( ; ) を付ける必要はありません。コメントは -- から行末まで、もしくは --[[ から ]] の間に記述します。

変数と関数は名前をつけて区別します。名前には英数字とアンダースコア _ が使えます。ただし、数字から始まる名前は定義できません。英大文字と英小文字は区別されるので、FOO と Foo と fooは異なる名前と判断されます。関数は Lua にあらかじめ用意されている「組み込み関数」のほかに、私達ユーザーが定義することもできます。もちろん「再帰定義」も可能です。

●数

それでは、Lua に用意されている基本的なデータ型について簡単に説明しましょう。Lua の数は浮動小数点数で表されます。これはC言語の倍精度浮動小数点数 (double) と同じで、範囲は絶対値で約 1e-307 から 1e+308 までです。簡単な例を示します。

> a = 100
> a
100
> b = 1.234
> b
1.234
> c = 10 + 20
> c
30
> d = 10 * 20
> d
200
> 10 - 20
-10
> 10 / 20
0.5

Lua はC言語と違い、変数のデータ型を宣言する必要はありません。変数に値をセットすることを「代入」といいます。代入には = を使います。これはC言語や Perl と同じです。対話モードで変数名を入力するとその値が表示されます。主な算術演算子を表 1 に示します。

表 1 : 算術演算子
操作結果
-x x を負にする
x + y x と y の和
x - y x と y の差
x * y x と y の積
x / y x 割る y の商
x % y x 割る y の剰余

●多重代入

演算子 = の左辺がカンマで区切られている場合、複数の代入を一度で行うことができます。これを多重代入といいます。簡単な例を示しましょう。

> a, b = 10, 20
> a
10
> b
20
> a, b = b, a
> a
20
> b
10

変数 a と b に 10 と 20 を代入しています。a と b の値を交換する場合、多重代入を使えば簡単に行うことができます。

●文字列

文字列 (string) はシングルクオート ' で囲むか、ダブルクオート " で囲んで表します。

> a = "hello, world"
> a
'hello, world'

変数 a に文字列 "hello, world" を代入しています。文字列には演算子 .. を適用することができます。

> "abc" .. "def"
'abcdef'

演算子 .. は文字列を連結した新しい文字列を作ります。

文字列には「エスケープシーケンス」を含めることができます。これは、画面に表示することができない文字を表すのに用いられる方法です。よく使われる記号に改行を表す \n とタブを表す \t があります。

> "abc\ndef"
'abc
def'
> "abc\tdef"
'abc    def'

この例はタブが 8 文字に設定されている場合です。

このほかに、Lua には文字列を操作する便利な関数が用意されています。また、正規表現も利用することができます。

●数と文字列の変換

Lua は Perl と同様に、数が必要な演算に文字列が与えられると、文字列を数に変換して処理を行います。数に変換できない場合はエラーになります。逆に、文字列が必要な演算で数が与えられると、数を文字列に変換して処理を行います。簡単な例を示しましょう。

> 1 + "2"
3
> "1" + "2"
3
> 1 + "x"
[string "local"]:1: attempt to perform arithmetic on a string value
> 'abc' .. 10
'abc10'
> 1 .. 2
'12'

●配列

「配列 (array) 」は複数のデータを格納するデータ構造です。配列に格納されたデータを要素といいます。特に、要素を一列に並べたものを 1 次元配列もしくは「ベクタ (vector) 」と呼びます。

配列の要素は 1 以上の整数で指定します。これを「添字 (subscripts) 」といいます。Lua の場合、C言語とは違って添字は 1 から始まることに注意してください。配列は中カッコ { と } で囲み、要素をカンマ ( , ) で区切って表します。{ } は要素が一つもない空の配列になります。

簡単な例を示します。

> ary1 = {10,20,30,40,50}
> ary1[1]
10
> ary1[5]
50
> ary1[5] = 100
> ary1[5]
100
> ary1
{10,20,30,40,100}

C言語は配列の大きさを宣言する必要がありますが、Lua の配列は大きさを宣言する必要はありません。配列の大きさは Lua が自動的に調整してくれます。大きさを自由に変えることができる配列を「可変長配列」といいます。Perl, Python, Ruby などのスクリプト言語でも可変長配列をサポートしています。

配列の要素には、いろいろなデータ型が混在していてもかまいません。また、要素に式を書くこともできます。

> ary2 = {"a", 1, "b", 2, "c", 3}
> ary2
{'a',1,'b',2,'c',3}
> ary2[1]
'a'
> ary2[6]
3
> ary3 = {1 + 2, 3 * 4 - 5}
> ary3
{3,7}
> ary3[1]
3
> ary3[2]
7

ary2 の配列は、0, 2, 4 番目の要素が文字列で、1, 3, 5 番目の要素が整数になっています。要素に式を書くと、その式の評価結果が要素になります。

配列は入れ子にすることができます。つまり、配列の要素に配列を入れてもかまいません。これで多次元配列を表すことができます。

> ary4 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
> ary4[1][1]
1
> ary4[2][1]
4
> ary4[3][3]
9

ary4 のように、配列の中に配列を入れることで 2 次元配列を表すことができます。ary4 の 1 番目の要素は配列 {1, 2, 3} で、その配列の 1 番目の要素は 1 です。この要素は角カッコを 2 つ使って ary4[1][1] とアクセスすることができます。最初の ary4[1] で 1 番目の配列を取り出し、その配列の 1 番目の要素を次の [1] で取り出します。

●連想配列 (ハッシュ)

配列が数値を使って要素を指定するのに対し、「連想配列 (ハッシュ) 」はキーというデータを使って要素を指定します。一般に、ハッシュのキーには文字列が用いられます。Lua の場合、配列はハッシュを使って実装されているので、キーが数値でも要素にアクセスすることができます。Lua の場合、配列やハッシュのことを「テーブル (table) 」と呼びますが、本稿では配列とハッシュを区別して呼ぶことにします。

ハッシュは中カッコ '{' と '}' で囲み、要素をカンマで区切って表します。要素は「キー = 値」で指定します。キーは名前で指定します。キーを省略した場合、キーは数値 (添字) になります。この場合、「キー = 値」の要素をすべて取り除くと配列の定義と同じになるので、添字はそれと同じ値になると考えてください。 { } は要素が一つもない空のハッシュになります。

簡単な例を示します。

> d = {foo = 10, bar = 20}
> d
{bar=20,foo=10}
> d["bar"]
20
> d["foo"]
10
> d.bar
20
> d.foo
10
> d["foo"] = 100
> d
{bar=20,foo=100}
> d.foo = 1000
> d
{bar=20,foo=1000}
> print(d["baz"])
nil
> d.baz = 1
> d.baz
1
> d["baz"] = nil
> d
{bar=20,foo=1000}

ハッシュのアクセスは配列と同様に角カッコ [ ] を使うか、"ハッシュ + ドット ( . ) + 名前" で行います。最初にハッシュを生成して変数 d にセットします。d["foo"] または d.foo でキー foo の値を取り出したり、そこに値を代入すれば、d["foo"] の値を書き換えることができます。

値を取り出すとき、キーが見つからない場合は nil という特別な値を返します。対話モードでは nil を表示しないので、関数 print を使って d["baz"] の値を表示しています。通常、nil は値がないことを表すために使われます。

新しいキー baz を追加する場合は、d["baz"] または d.baz に値を代入すると、ハッシュに "baz" とその値が追加されます。nil を代入すると、ハッシュからキーを削除することができます。


Lua の基礎知識 (制御構造編)

Lua の制御構造を簡単に説明します。

●条件分岐

条件分岐には if を使います。Lua の if は真偽の判定が他のスクリプト言語と少し異なります。Lua の場合、真偽を表すデータ型 (boolean) として true と false が用意されていますが、nil と false を偽と判定し、それ以外の値を真と判定します。数値 0 や空文字列 "" も真と判定されるので注意してください。

if の構文を示します。

if test then 処理A else 処理B end
if test_A then  処理A  elseif test_B then 処理B else 処理C end

条件部 test を実行し、その結果が真であれば 処理A を実行します。そうでなければ 処理B を実行します。then と else, else と end の間には複数の処理を記述することができます。これをブロック (block) といいます。1 行に複数の処理を記述する場合はセミコロン ( ; ) で区切ります。Lua の場合、do 処理A; 処理B; ... end のように do と end でブロックを明示することができます。なお、else ブロックは省略することができます。

また、elseif を使うことで、if を連結することができます。test_A が偽の場合は、次の elseif の条件 test_B を実行します。この結果が真であれば処理B を実行します。そうでなければ、else ブロックの処理C を実行します。else if はいくつでもつなげることができます。

●比較演算子と論理演算子

Lua には表 2 に示す比較演算子が用意されています。

表 2 : 比較演算子
演算子意味
== 等しい
~= 等しくない
<より小さい
>より大きい
<=より小さいか等しい
>=より大きいか等しい

Lua は数値だけではなく、文字列の比較にも用いることができます。データ型が異なる場合は異なる値と判断されます。型変換は行われないので注意してください。

簡単な例を示しましょう。

> 1 < 2
true
> 1 > 2
false
> 1 == 1
true
> 1 ~= 1
false
> 1 == "1"
false
> "abc" < "def"
true
> "abc" > "def"
false
> "abc" == "abc"
true
> "abc" ~= "abc"
false

Lua には表 3 に示す論理演算子があります。

表 3 : 論理演算子
操作意味
not x x の否定(真偽の反転)
x and y x が真かつ y が真ならば真
x or y x が真または y が真ならば真

簡単な使用例を示しましょう。

> not true
false
> not false
true
> true and true
true
> true and false
false
> true or true
true
> true or false
true
> false or true
true
> false or false
false

論理積 and は、左項が偽ならば右項を評価せずに偽を返します。論理和 or は、左項が真ならば右項を評価せずに真を返します。

●繰り返し

繰り返しは同じ処理を何度も実行することです。まずは簡単な繰り返しから紹介しましょう。

while test do 処理A; 処理B; ... end

while 文は test が真であるあいだ、ブロック内の処理を繰り返し実行します。

簡単な例を示しましょう。hello, world を 10 回表示します。

> n = 0
> while n < 10 do print("hello, world"); n = n + 1 end
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world

print はデータを標準出力 (画面) に出力する関数です。print はその後に改行を行います。変数 n を 0 に初期化し、n の値が 10 よりも小さいあいだ処理を繰り返します。n の値はブロックを実行するたびに +1 されていくので、n が 10 になった時点で繰り返しを終了します。

このほかに、repeat - until 文もあります。

repeat 処理A; ...; 処理Z until test

while 文と違って、repeat - until 文は最初にブロック内の処理を実行し、test が偽であるあいだブロックを繰り返し実行します。つまり、test が繰り返しの終了条件を表します。簡単な例を示します。

> n = 0
> repeat print("hello, world"); n = n + 1 until n == 10
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world

次は for 文を説明します。Lua の for 文は数値用と汎用の 2 通りの形式があります。ここでは数値用の for 文を説明します。

for 変数名 = 式1, 式2, 式3 do 処理A; ...; 処理Z end

式 1 が初期値、式 2 が終了値、式 3 が増分値を表します。式 3 を省略すると増分値は 1 になります。式 1, 2, 3 の値が m, n, a とすると、m, m + a, m + 2 * a, ... の順で変数に値をセットしてブロックを繰り返し実行します。変数の値が n より大きくなると繰り返しを終了します。

簡単な使用例を示しましょう。

> for n = 1, 10 do print(n) end
1
2
3
4
5
6
7
8
9
10
> for n = 1, 10 do print("hello, world") end
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world

for n = 1, 10 do ... end は n を 1 に初期化し、n の値を +1 しながらブロックの処理を繰り返します。n が 11 になると終了値 10 よりも大きくなるので、繰り返しを終了します。

●繰り返しの制御

while 文, repeat 文、for 文は break 文によって繰り返しを脱出することができます。ただし、Lua には contiune 文がありません。ご注意ください。break 文の動作を図 1 に示します。

#
# break 文の動作
#
while test_a do
    処理A
    処理B
    if(test_c) then
        break  ──────┐
    end                    │
    処理C                  │
}                          │
 ←────────────┘
処理D

図 1 : break 文の動作

test_c が真で break 文が実行されると、それ以降の処理を実行せずに while 文, repeat 文, for 文の繰り返しを脱出します。図 1 では、break 文で while 文の繰り返しを脱出すると、while 文の次の処理 D が実行されます。

Lua の場合、break 文はブロックの最後にしか書くことが許されません。ブロックの途中で break 文を使いたい場合は、do break end のように新しいブロックの中で break を使ってください。

●配列とハッシュの操作

配列は [ ] を使ってアクセスするだけではありません。Lua には便利な関数や演算子が用意されています。主な操作を表 4 に示します。

表 4 : 配列に適用できる主な操作
操作意味
#ary配列の大きさを求める
table.insert(ary, [pos,] x)pos 番目に要素を追加する
pos が省略された場合は末尾に追加する
table.remove(ary, [pos,])pos 番目から要素を削除する
pos が省略された場合は末尾から削除する

ary は配列を表します。配列の操作関数はハッシュ table にまとめられています。ハッシュ内の関数は "ハッシュ名" + "." + "関数名" でアクセスすることができます。つまり、関数名がハッシュのキーで、値が関数の実体になります。

# は長さ演算子です。長さ演算子は添字 n の値が nil 以外で、かつ n + 1 の値が nil となる n を返します。Lua の場合、nil は値がないことを表すデータです。配列の要素に nil をセットすると、その要素はなくなったことになります。つまり、配列の途中で穴をあけた状態になるのです。このような状態を作らなければ、長さ演算子で配列の大きさを求めることができます。

簡単な例を示します。

> a = {}
> table.insert(a, 1)
> a
{1}
> table.insert(a, 2)
> a
{1,2}
> table.insert(a, 3)
> table.insert(a, 4)
> a
{1,2,3,4}
> table.remove(a)
4
> a
{1,2,3}
> table.remove(a)
3
> a
{1,2}
> table.remove(a)
2
> a
{1}
> table.remove(a)
1
> a
{}

ハッシュのキーと値は for - in 文で求めることができます。

for k, v in pairs(ary) do 処理A; ...; 処理Z end

for - in 文は関数 pairs が返すイテレータ (ハッシュからキーと値を順番に取り出す関数) を呼び出し、その返り値を変数 k, v にセットして、ブロックの処理を繰り返し実行します。キーは変数 k に、値は変数 v にセットされます。なお、pairs のかわりに関数 ipairs を使うと、配列の添字と値を順番に取り出すことができます。

簡単な例を示します。

> a = {foo=10, bar=20, baz=30}
> a
{baz=30,bar=20,foo=10}
> for k, v in pairs(a) do print(k); print(v) end
baz
30
bar
20
foo
10
> b = {10,20,30}
> b
{10,20,30}
> for k, v in ipairs(b) do print(k); print(v) end
1
10
2
20
3
30

●素数を求める

最後に簡単な例題として、素数を求めるプログラムを作ってみましょう。いちばん簡単な方法は、奇数 3, 5, 7, 9, ...をそれまでに見つかった素数で割ってみることです。見つけた素数は配列に格納しておけばいいでしょう。プログラムをリスト 3 に示します。

リスト 3 : 素数を求める(1)

prime_list = {2}

for x = 3, 100, 2 do
  a = true
  for y = 1, #prime_list do
    z = prime_list[y]
    if x % z == 0 then
      a = false
      break
    end
  end
  if a then
    table.insert(prime_list, x)
  end
end

for x = 1, #prime_list do
  io.write(prime_list[x])
  io.write(" ")
end

変数 prime_list は素数の配列で {2} に初期化します。奇数の生成は for 文を使うと簡単です。変数 x を 3 に初期化し、更新処理で +2 していけばいいわけです。次の for 文で prime_list から素数を取り出して変数 z にセットします。

ここで変数 a の使い方がポイントになります。最初に a を true に初期化しておきます。x % z が 0 ならば x は素数ではないので、a を false に書き換えてから break します。そして、for ループが終了した後、変数 a をチェックして true であれば x を insert で prime_list に追加します。最後 に for 文で prime_list を表示します。write はデータを表示する関数で、print とは違って改行を行いません。

それでは実行してみましょう。プログラムをファイル prime.lua に保存して、コマンドプロンプトで lua prime.lua を実行してください。

C>lua prime.lua
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

100 以下の素数は全部で 25 個あります。

ところで、この方法には無駄があります。x が素数か判別するため、x より小さい素数で割り切れるか調べていますが、実は√x より小さい素数を調べるだけでいいのです。次のリストを見てください。

リスト 4 : 素数を求める(2)

prime_list = {2}

for x = 3, 100, 2 do
  a = true
  for y = 1, #prime_list do
    z = prime_list[y]
    if z * z > x then
      break
    end
    if x % z == 0 then
      a = false
      break
    end
  end
  if a then
   table.insert(prime_list, x)
  end
end

for x = 1, #prime_list do
  io.write(prime_list[x])
  io.write(" ")
end

z > √x のかわりに z * z > x をチェックし、真であれば break で for 文を脱出します。これでリスト 3 よりも高速に素数を求めることができます。

ところで、リスト 3, 4 のプログラムはちょっとわかりにくいですね。この場合、関数を使うとわかりやすいプログラムを作ることができます。関数は次回で詳しく説明します。


Copyright (C) 2011 Makoto Hiroi
All rights reserved.

[ PrevPage | L u a | NextPage ]