M.Hiroi's Home Page

Lua Programming

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

[ PrevPage | L u a | NextPage ]

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

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

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

●数

それでは、Lua に用意されている基本的なデータ型について簡単に説明しましょう。Lua の数には「整数 (integer)」と「浮動小数点数 (floating point number)」の二種類があります。整数は ver 5.3.0 からサポートされました。それ以前のバージョンでは整数はなく、数は浮動小数点数になります。通常、整数の範囲は -9223372036854775808 から 9223372036854775807 (64 bit) までです。浮動小数点数は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
> 10 // 20
0
> 20 / 10
2.0
> 20 // 10
2

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 の商 (小数点以下切り捨て)
x % y x 割る y の剰余
x ^ y x の y 乗 (累乗)

基本的に、整数と整数の演算結果は整数に、それ以外の演算結果は浮動小数点数になります。除算 / と累乗 ^ は引数を浮動小数点数に変換し、演算結果も浮動小数点数になります。

●ビット演算子

整数の導入とともに Lua はビット演算子をサポートするようになりました。ビット演算子は引数を整数に変換して演算処理を行い、その結果を整数として返します。Lua のビット演算子を表 2 に示します。

表 2 : ビット演算子
演算子操作
x & y ビットごとの論理積
x | y ビットごとの論理和
x ~ y ビットごとの排他的論理和
~x ビットごとの否定
x << y x を y ビット左シフト
x >> y x を y ビット右シフト

演算子 & はビットごとの論理積を返します。

> 5 & 3
1
     0101
 AND 0011
---------
     0001

演算子 l はビットごとの論理和を返します。

> 5 | 3
7
    0101
 OR 0011
--------
    0111

二項演算子 ~ はビットごとの排他的論理和を返します。

> 5 ~ 3
6
     0101
 XOR 0011
---------
     0110

単項演算子 ~ はビットごとの論理的な否定を返します。

> ~1
-2
> ~0
-1

<<, >> はビットをシフトする演算子です。左シフトの場合、下位ビットには 0 が挿入されます。右シフトの場合、上位ビットに 0 が挿入されます。したがって、負の整数を右シフトすると正の整数になります。いわゆる「算術シフト」ではないので注意してください。

> 1 << 8
256
> 1 << 16
65536
> 256 >> 8
1
> 65536 >> 16
1
> 1 << 63
-9223372036854775808
> (1 << 63) >> 1
4611686018427387904

●多重代入

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

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

> 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.0
> "1" + "2"
3.0
> 1 + "x"
stdin:1: attempt to perform arithmetic on a string value
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
> 'abc' .. 10
abc10
> 1 .. 2
12

●配列

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

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

簡単な例を示します。

> ary1 = {1, 2, 3, 4, 5}
> ary1
table: 0x7fffe8e9cd70
> ary1[1]
1
> ary1[5]
5
> ary1[5] = 100
> ary1[5]
100

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

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

> ary2 = {"a", 1, "b", 2, "c", 3}
> ary2
table: 0x7fffe8e97680
> ary2[1]
a
> ary2[6]
3
> ary3 = {1 + 2, 3 * 4 - 5}
> ary3
table: 0x7fffe8e90cf0
> 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
table: 0x7fffe8e969f0
> d["bar"]
20
> d["foo"]
10
> d.bar
20
> d.foo
10
> d["foo"] = 100
> d["foo"]
100
> d.foo = 1000
> d.foo
1000
> d["baz"]
nil
> d.baz = 1
> d.baz
1
> d.baz = nil
> d.baz
nil

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

値を取り出すとき、キーが見つからない場合は nil という特別な値を返します。通常、nil は値がないことを表すために使われます。新しいキー baz を追加する場合は、d["baz"] または d.baz に値を代入すると、ハッシュに "baz" とその値が追加されます。nil を代入すると、ハッシュからキーを削除することができます。


初版 2011 年 4 月 16 日
改訂 2019 年 12 月 28 日

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

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

●条件分岐

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

if の構文を示します。

(1) if test then 処理A end
(2) if test then 処理A else 処理B end

                    図 1 : if 文 (その1)

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

また、elseif を使うことで、if を連結することができます。

if test_a then 処理A elseif test_b then 処理B else 処理C end

                図 2 : if 文 (その2)

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

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

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

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

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 には表 4 に示す論理演算子があります。

表 4 : 論理演算子
操作意味
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

        図 3 : while 文

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

        図 4 : for 文 (その1)

式 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 文の動作を図 5 に示します。

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

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

●配列とハッシュの操作

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

表 5 : 配列に適用できる主な操作
操作意味
#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 = {}
> #a
0
> table.insert(a, 1)
> a[1]
1
> table.insert(a, 2)
> a[2]
2
> table.insert(a, 3)
> a[3]
3
> table.insert(a, 4)
> a[4]
4
> #a
4
> table.remove(a)
4
> table.remove(a)
3
> table.remove(a)
2
> table.remove(a)
1
> #a
0

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

 for 変数, ... in 式 do 処理A; ...; 処理Z end

        図 6 : for 文 (その2)

in の後ろの式には「イテレータ (iterator)」またはイテレータを生成する関数を指定します。イテレータはデータ構造 (テーブルなど) の要素を順番にアクセスしていく関数です。関数 pairs はテーブルからキーと値を順番に返すイテレータを生成します。

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

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

簡単な例を示します。

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

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

●平均値を求める

それでは簡単な例題として、配列に格納されているデータの平均値を求めてみましょう。次のリストを見てください。

リスト 1 : 平均値を求める

height = {
  148.7, 149.5, 133.7, 157.9, 154.2, 147.8, 154.6, 159.1, 148.2, 153.1,
  138.2, 138.7, 143.5, 153.2, 150.2, 157.3, 145.1, 157.2, 152.3, 148.3,
  152.0, 146.0, 151.5, 139.4, 158.8, 147.6, 144.0, 145.8, 155.4, 155.5,
  153.6, 138.5, 147.1, 149.6, 160.9, 148.9, 157.5, 155.1, 138.9, 153.0,
  153.9, 150.9, 144.4, 160.3, 153.4, 163.0, 150.9, 153.3, 146.6, 153.3,
  152.3, 153.3, 142.8, 149.0, 149.4, 156.5, 141.7, 146.2, 151.0, 156.5,
  150.8, 141.0, 149.0, 163.2, 144.1, 147.1, 167.9, 155.3, 142.9, 148.7,
  164.8, 154.1, 150.4, 154.2, 161.4, 155.0, 146.8, 154.2, 152.7, 149.7,
  151.5, 154.5, 156.8, 150.3, 143.2, 149.5, 145.6, 140.4, 136.5, 146.9,
  158.9, 144.4, 148.1, 155.5, 152.4, 153.3, 142.3, 155.3, 153.1, 152.3
}

s = 0.0
for i = 1, #height do
  s = s + height[i]
end
print(s / #height)

配列 height には生徒 100 人の身長が格納されています。平均値を求めるには、これらのデータの合計値を求めて、それを人数で割り算すればいいですね。プログラムは簡単です。変数 s にデータの合計値を求めます。s は 0.0 に初期化しておきます。あとは for ループで配列 height の要素を順番に取り出して、それを変数 s に加算していきます。最後に s / #height を計算すれば平均値を求めることができます。

それでは実行してみましょう。

$ lua5.3 mean.lua
150.627

●数値積分

次は数値積分で円周率 \(\pi\) を求めてみましょう。区間 [a, b] の定積分 \(\int_a^b f(x)\,dx\) を数値的に求めるには、区間を細分して小区間の面積を求めて足し上げます。小区間の面積を求める一番簡単な方法は長方形で近似することです。この場合、3 つの方法が考えられます。

  1. (b - a) * f(a)
  2. (b - a) * f(b)
  3. (b - a) * f((a + b) / 2)

1 は左端の値 f(a) を、2 は右端の値 f(b) を、3 は中間点の値 f((a + b) / 2) を使って長方形の面積を計算します。この中で 3 番目の方法が一番精度が高く、これを「中点則」といいます。このほかに、台形で近似する「台形則」や、2 次近似で精度を上げる「シンプソン則」という方法があります。

それでは実際に、中点則で \(\pi\) の値を求めてみましょう。\(\pi\) は次の式で求めることができます。

\( \pi = \displaystyle \int_0^1 \dfrac{4}{1 + x^2}\,dx \)

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

リスト 2 : 数値積分で円周率を求める (midpoint.lua)

n = 10000
w = 1.0 / n
s = 0.0
for i = 1, n do
  x = (i - 0.5) * w
  s = s + 4.0 / (1.0 + x * x)
end

print(s * w)

変数 n が分割数です。最初に小区間の幅を求めて変数 w にセットします。面積は変数 s にセットします。次の for ループで区間 [0, 1] を n 個に分割して面積を求めます。

最初に x 座標を計算します。中間点を求めるため、変数 i を 1 から始めて、x 座標を次の式で求めます。

x = (i - 0.5) * w

たとえば、変数 i が 1 の場合は 0.5 になるので、x は区間 [0 * w, 1 * w] の中間点になります。あとは、4 / (1 + x * x) を計算して s に加算します。最後に s に w を掛け算して全体の面積を求めます。

実行結果を示します。

$ lua5.3 midpoint.lua
3.1415926544231

●素数を求める

最後にもう一つ簡単な例題として、素数を求めるプログラムを作ってみましょう。いちばん簡単な方法は、奇数 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 を実行してください。

$ lua5.3 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 より小さい素数で割り切れるか調べていますが、実は \(\sqrt 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 > \(\sqrt x\) のかわりに z * z > x をチェックし、真であれば break で for 文を脱出します。これでリスト 3 よりも高速に素数を求めることができます。

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


初版 2011 年 4 月 16 日
改訂 2019 年 12 月 28 日

Copyright (C) 2011-2019 Makoto Hiroi
All rights reserved.

[ PrevPage | L u a | NextPage ]