Lua/チュートリアル
Luaは、軽量で高速なスクリプト言語であり、広く使われているプログラミング言語の一つです。Luaは、簡単に学べる言語でありながら、多くの機能が備わっており、ゲーム開発やWebアプリケーション、組み込みシステムなど、様々な分野で使われています。
本チュートリアルでは、Luaの基礎的な構文や機能について説明し、Luaを使ったプログラミングについての知識を身に付けることを目的としています。本チュートリアルは、初心者から中級者までを対象にしており、Luaの基礎から始めて、徐々に応用的な内容に移っていきます。
Luaの学習は、プログラミング初心者にも取り組みやすく、さまざまなアプリケーションで活用できることから、将来性が高く注目を集めています。本チュートリアルを通して、Luaの基礎を学び、Luaを使ったプログラミングの魅力に触れていただければ幸いです。
イントロダクション
編集Luaとは何か?
編集Luaは、1993年にブラジルで開発された軽量で高速なスクリプト言語で、オープンソースであることが特徴です。Luaは、スクリプト言語としては比較的小さく、C言語で書かれているため、高速でメモリ効率が良いという特徴があります。
Luaの特徴
編集Luaの特徴は、軽量で高速なことや、C言語との親和性が高いことに加え、以下のようなものが挙げられます。
- 拡張性が高い:Luaは、C言語で書かれているため、C言語との連携が容易であり、また、Luaの言語仕様自体も拡張性が高く、機能を自由に追加できます。
- 埋め込みが容易:Luaは、他のプログラミング言語に埋め込んで使用することができ、多くのアプリケーションで利用されています。
- 簡潔でわかりやすい構文:Luaの構文は、C言語やJavaなどの言語に似ており、学習が比較的容易です。
Luaの用途
編集Luaは、以下のような分野で使われています。
- ゲーム開発:Luaは、多くのゲームエンジンでスクリプト言語として利用されています。UnityやCorona SDK、LÖVEなど、多くのゲームエンジンがLuaを採用しています。
- Webアプリケーション:Luaは、Webアプリケーションの開発にも利用されています。OpenRestyやLapisなど、Luaを利用したWebフレームワークも存在します。
- 組み込みシステム:Luaは、組み込みシステムのプログラミングにも利用されています。Luaは、メモリ効率が高いため、小規模なシステムでも利用することができます。
Luaは、その小ささや高速性、拡張性、埋め込み性などの特徴から、様々な分野で利用されています。
インストールとセットアップ
編集Luaのインストール方法
編集Luaは、公式サイト( https://www.lua.org/download.html )から、ソースコードやバイナリファイルをダウンロードすることができます。 また、一部のLinuxディストリビューションやBSD系UNIXでは、パッケージマネージャを利用して簡単にインストールすることができます。
Luaの実行方法
編集インストールが完了したら、コマンドラインから以下のように入力することで、Luaの対話モードを起動することができます。
$ lua
対話モードは、コマンドラインから一行ずつLuaのコードを入力して、即座に評価することができます。
また、Luaのプログラムをファイルに保存して実行することもできます。例えば、以下のようなスクリプトファイルを作成し、"hello.lua"という名前で保存します。
- hello.lua
print("Hello, world!")
そして、コマンドラインから以下のように入力することで、スクリプトを実行することができます。
$ lua
テキストエディターの選択
編集Luaのプログラムを書くためには、テキストエディターが必要です。 多くのテキストエディターがLuaのシンタックスハイライトをサポートしていますが、特におすすめなのは、ZeroBrane StudioやVisual Studio Codeなどの、Lua開発に特化したエディターです。 これらのエディターには、デバッグ機能やコード補完機能など、開発を効率化する機能が多数備わっています。
変数とデータ型
編集変数の宣言
編集Luaでは、変数名の前にlocalキーワードを付けることで、その変数をローカルスコープで宣言することができます。例えば、以下のようにして、xという変数を宣言することができます。
local x
変数に初期値を代入する場合は、以下のように宣言と代入を同時に行うことができます。
local x = 42
データ型の種類
編集Luaには、以下のようなデータ型があります。
- nil: 値が存在しないことを表す
- boolean: trueまたはfalseの値を表す
- number: 数値を表す
- string: 文字列を表す
- function: 関数を表す
- table: テーブルを表す(配列や連想配列など、複数の値をまとめて扱うデータ構造)
まず、Lua 5.2までは全ての数値が倍精度浮動小数点数型 (double) で表現されていましたが、Lua 5.3からは整数と実数の区別が明確になりました。 整数は64ビット符号付き整数型 (long long) で表現されます。これにより、整数演算がより高速になり、メモリ消費も減ります。
for i = 0, 64 do n = 2 ^ i print(i, n, type(n), math.type(n), math.type(math.tointeger(n))) end
- 実行結果
0 1.0 number float integer 1 2.0 number float integer 2 4.0 number float integer 3 8.0 number float integer 4 16.0 number float integer 5 32.0 number float integer 6 64.0 number float integer 7 128.0 number float integer 8 256.0 number float integer 9 512.0 number float integer 10 1024.0 number float integer 11 2048.0 number float integer 12 4096.0 number float integer 13 8192.0 number float integer 14 16384.0 number float integer 15 32768.0 number float integer 16 65536.0 number float integer 17 131072.0 number float integer 18 262144.0 number float integer 19 524288.0 number float integer 20 1048576.0 number float integer 21 2097152.0 number float integer 22 4194304.0 number float integer 23 8388608.0 number float integer 24 16777216.0 number float integer 25 33554432.0 number float integer 26 67108864.0 number float integer 27 134217728.0 number float integer 28 268435456.0 number float integer 29 536870912.0 number float integer 30 1073741824.0 number float integer 31 2147483648.0 number float integer 32 4294967296.0 number float integer 33 8589934592.0 number float integer 34 17179869184.0 number float integer 35 34359738368.0 number float integer 36 68719476736.0 number float integer 37 137438953472.0 number float integer 38 274877906944.0 number float integer 39 549755813888.0 number float integer 40 1099511627776.0 number float integer 41 2199023255552.0 number float integer 42 4398046511104.0 number float integer 43 8796093022208.0 number float integer 44 17592186044416.0 number float integer 45 35184372088832.0 number float integer 46 70368744177664.0 number float integer 47 1.4073748835533e+14 number float integer 48 2.8147497671066e+14 number float integer 49 5.6294995342131e+14 number float integer 50 1.1258999068426e+15 number float integer 51 2.2517998136852e+15 number float integer 52 4.5035996273705e+15 number float integer 53 9.007199254741e+15 number float integer 54 1.8014398509482e+16 number float integer 55 3.6028797018964e+16 number float integer 56 7.2057594037928e+16 number float integer 57 1.4411518807586e+17 number float integer 58 2.8823037615171e+17 number float integer 59 5.7646075230342e+17 number float integer 60 1.1529215046068e+18 number float integer 61 2.3058430092137e+18 number float integer 62 4.6116860184274e+18 number float integer 63 9.2233720368548e+18 number float nil 64 1.844674407371e+19 number float nil
64ビット符号付き整数型の値は数値リテラルの範囲外である場合、オーバーフローして扱われます。 例えば、0x8000000000000000 は 64 ビット符号付き整数型の範囲を超えているため、正しく解釈されません。
また、整数型と実数型の演算は実数型と同じように扱われるため、整数型にキャストしなければならない場合もあります。変数の代入と操作
編集変数に値を代入する場合は、以下のようにして行います。
local x x = 42
また、代入された値を取り出して操作する場合は、以下のようにして行います。
local x = 42 x = x + 1 -- xの値を1増やす
文字列の場合は、以下のようにして連結することができます。
local str1 = "Hello, " local str2 = "world!" local message = str1 .. str2 -- "Hello, world!"という文字列が作られる
また、テーブルの場合は、以下のようにして要素を追加したり、削除したりすることができます。
local tbl = {} tbl[1] = "apple" tbl[2] = "banana" tbl[3] = "orange" table.remove(tbl, 2) -- tblから"banana"を削除する
これらの操作を利用して、Luaで様々な処理を行うことができます。
制御構文
編集以下は、Luaの制御構造のいくつかの例です。
-- 条件分岐 if x > 0 then print("x is positive") elseif x == 0 then print("x is zero") else print("x is negative") end -- ループ ---- whileループ while x < 10 do print(x) x = x + 1 end ---- repeat-untilループ repeat print(x) x = x + 1 until x == 10 ---- forループ -- 数字のステップは1 for i=1, 10 do print(i) end -- 数字のステップを変更 for i=1, 10, 2 do print(i) end -- 配列の要素を取得 fruits = {"apple", "banana", "pear"} for i, fruit in ipairs(fruits) do print(i, fruit) end -- ジャンプ -- break ---- ループを途中で終了する場合に使用します。 while true do if x == 10 then break end print(x) x = x + 1 end -- goto ---- ラベルにジャンプする場合に使用できます。なるべく使用を避けるようにしましょう。 ::label:: print("Hello, world!") goto label
条件分岐
編集条件分岐にはif
文を使用します。以下は、x
が正の数であれば、"positive"
と表示するプログラムの例です。
local x = 5 if x > 0 then print("positive") end
また、if
文の後にelseif
を続けて書くことで、複数の条件分岐を行うことができます。
local x = 5 if x > 0 then print("positive") elseif x == 0 then print("zero") else print("negative") end
ループ処理
編集Luaには、以下の3種類のループ処理があります。
whileループ
編集while
ループは、条件式がtrue
の間、繰り返し処理を行います。
local i = 1 while i <= 10 do print(i) i = i + 1 end
repeat-untilループ
編集repeat-until
ループは、条件式がtrue
になるまで、繰り返し処理を行います。
local i = 1 repeat print(i) i = i + 1 until i > 10
forループ
編集for
ループは、あらかじめ指定した回数分、繰り返し処理を行います。以下は、1
から10
までの数字を順番に表示するプログラムの例です。
for i = 1, 10 do print(i) end
増分
編集for i = 1, 10, 2 do print(i) end
このコードは、Luaのforループを使用して1から10までの数値を2ずつ増やしながら順に取り出し、それぞれを出力するプログラムです。
forループの書式は、以下のようになっています。
for 初期値, 終了値[, 増分] do -- ループ処理 end
初期値から始まり、終了値に達するまで、指定された増分ずつ値を増やしながらループ処理が実行されます。
増分は省略可能で、その場合は 1 が仮定されます。
今回のコードでは、初期値を1、終了値を10、増分を2としているため、1, 3, 5, 7, 9の5つの数値が順に取り出され、それぞれがprint()
関数で出力されます。
具体的に実行すると、以下のような結果が得られます。
1 3 5 7 9
break文
編集break
文を使用することで、ループ処理を途中で終了することができます。
以下は、1
から10
までの数字を順番に表示し、5
になったらループを終了するプログラムの例です。
for i = 1, 10 do print(i) if i >= 5 then break end end
continue
文はありません。代わりに、break
文と条件分岐を使用して、同じような処理を実現することができます。
例えば、以下のようにif
文を使用して、特定の条件が成立した場合には処理をスキップすることができます。
for i = 1, 10 do if i % 2 == 0 then -- iが偶数の場合はスキップ goto continue end print(i) ::continue:: end
ただし、goto
文は使用しない方が良いとされているため、代替案として以下のようにif
文を入れ子にする方法もあります。
for i = 1, 10 do if i % 2 ~= 0 then -- iが奇数の場合のみ処理を実行 print(i) end end
continue
文を直接サポートしていませんが、代替手段を用いることで同様の処理を実現することができます。関数の定義と呼び出し
編集関数を定義するには、以下のようにfunction
キーワードを使用します。また、引数を渡す場合は、()
の中に引数名を書きます。
function greet(name) print("Hello, " .. name .. "!") end
関数を呼び出すには、関数名とともに引数を渡します。以下は、greet
関数を呼び出すプログラムの例です。
greet("Alice") -- "Hello, Alice!"と表示される
関数の引数
編集Luaにはキーワード引数がなく、関数の引数は位置引数として渡されます。
可変長引数を受け取るため、Luaでは ... という構文があります。これは関数の引数リストの最後に置かれます。可変長引数は、呼び出し側が渡す引数の数によって異なります。関数内では、可変長引数はテーブルとして扱われます。たとえば、以下のようにして可変長引数を受け取る関数を定義できます。
function myfunc(...) for i,v in ipairs({...}) do print(i, v) end end
残余引数はありませんが、可変長引数を受け取った後に、任意の位置引数を受け取ることができます。たとえば、以下のようにして x という位置引数を受け取る関数を定義できます。
function myfunc(..., x) local varargs = {...} -- 可変長引数 print("varargs:", unpack(varargs)) print("x:", x) end
関数もオブジェクト
編集Luaでは、関数もオブジェクトの一種であるため、変数に関数を代入することもできます。
local square = function(x) return x * x end print(square(5)) -- "25"と表示される
関数の返り値を取得するには、return
キーワードを使用します。
Luaの関数は、以下のような特徴があります。
- 引数の個数が可変長であることができます。すなわち、関数を呼び出すときに渡す引数の個数を自由に指定できます。
- 関数の戻り値が複数あることができます。すなわち、関数から複数の値を返すことができます。
- 関数は、変数に代入したり、テーブルの値として保存したりすることができます。
Luaの関数は、以下のように定義します。
function 関数名(引数1, 引数2, ...) -- 関数の処理 return 戻り値1, 戻り値2, ... end
例えば、以下のような関数を定義することができます。
function add(a, b) return a + b end
この関数は、2つの引数を受け取り、それらを加算した結果を返します。関数を呼び出すときは、以下のようにします。
local result = add(1, 2) print(result) -- 3
また、複数の戻り値を返すこともできます。例えば、以下のような関数を定義することができます。
function getMinMax(numbers) local min = numbers[1] local max = numbers[1] for i = 2, #numbers do if numbers[i] < min then min = numbers[i] end if numbers[i] > max then max = numbers[i] end end return min, max end
この関数は、配列 numbers
に格納されている数値の中で、最小値と最大値を求めます。関数を呼び出すときは、以下のようにします。
local numbers = {5, 3, 9, 1, 7} local min, max = getMinMax(numbers) print("min:", min) -- 1 print("max:", max) -- 9
文字列操作
編集Luaは文字列操作に便利な機能を提供しています。文字列は、クォーテーションで囲んだ文字のシーケンスです。ダブルクォーテーションまたはシングルクォーテーションのどちらでも表現できます。
文字列の作成と操作
編集文字列は、..
演算子で連結することができます。
str1 = "Hello" str2 = "World" str3 = str1 .. " " .. str2 -- "Hello World"
文字列を部分的に取り出すには、string.sub
関数を使用します。
str = "Hello World" substr = string.sub(str, 1, 5) -- "Hello"
また、文字列を逆順にするには、string.reverse
関数を使用します。
str = "Hello" reverse_str = string.reverse(str) -- "olleH"
文字列のフォーマット
編集Luaは、C言語と同様に、文字列のフォーマットに便利な機能を提供しています。string.format
関数を使用して、文字列を特定の形式に整形することができます。
name = "John" age = 30 height = 180.5 str = string.format("My name is %s, I'm %d years old, and my height is %.1f cm", name, age, height) print(str) -- "My name is John, I'm 30 years old, and my height is 180.5 cm"
この例では、%s
は文字列、%d
は10進数、%.1f
は小数点以下1桁の浮動小数点数を表します。string.format
関数に渡す引数は、フォーマット文字列内の変数と一致する順序で指定する必要があります。
これらの機能を駆使することで、Luaで柔軟な文字列操作が可能になります。
テーブルと配列
編集テーブルとは何か?
編集Luaにおいて、テーブルは複数の値を格納するためのデータ構造の1つです。テーブルは配列や辞書のような形式で、キーと値をペアとして持ちます。
テーブルの作成と初期化
編集テーブルは、{}
で作成することができます。以下のように、要素をカンマで区切って列挙することで初期化することができます。
-- テーブルの作成と初期化 fruits = {'apple', 'banana', 'orange'}
また、以下のように、インデックスを指定して要素を追加することもできます。
-- テーブルの作成と初期化 fruits = {} fruits[1] = 'apple' fruits[2] = 'banana' fruits[3] = 'orange'
テーブルの操作
編集テーブルに格納された要素には、以下のような方法でアクセスすることができます。
- インデックスを指定して要素を取り出す
- forループを使用して、全ての要素にアクセスする
table.insert()
関数を使用して、テーブルの末尾に要素を追加するtable.remove()
関数を使用して、指定した位置の要素を削除するtable.sort()
関数を使用して、テーブルをソートする
以下に、それぞれの操作の例を示します。
-- インデックスを指定して要素を取り出す print(fruits[1]) -- "apple" -- forループを使用して、全ての要素にアクセスする for i, fruit in ipairs(fruits) do print(i, fruit) end -- table.insert()関数を使用して、テーブルの末尾に要素を追加する table.insert(fruits, 'grape') -- table.remove()関数を使用して、指定した位置の要素を削除する table.remove(fruits, 2) -- table.sort()関数を使用して、テーブルをソートする table.sort(fruits)
具体的には、テーブルにメソッドや属性を格納してオブジェクトを表現します。オブジェクトの作成には、テーブルを複製して必要な属性を追加したり、既存のテーブルに属性を追加することで実現できます。
例えば、以下のようにPersonというクラスを表現することができます。
-- Personクラスを表現するテーブル Person = {} -- コンストラクタ function Person:new(name, age) local obj = { name = name, age = age } setmetatable(obj, self) self.__index = self return obj end -- インスタンスメソッド function Person:sayHello() print("Hello, my name is " .. self.name .. " and I am " .. self.age .. " years old.") end -- インスタンス生成 local p1 = Person:new("John", 30) -- メソッド呼び出し p1:sayHello() -- "Hello, my name is John and I am 30 years old."
この例では、Personというテーブルを定義して、その中にnewというコンストラクタとsayHelloというインスタンスメソッドを定義しています。newはテーブルを生成するためのメソッドであり、setmetatableとself.__indexを使って、オブジェクトがPersonテーブルを継承するようにしています。また、インスタンスメソッドであるsayHelloは、オブジェクトの属性を参照してメッセージを表示するメソッドです。
このように、Luaではテーブルを使ってオブジェクトを表現することができます。プロトタイプベースのオブジェクト指向は、クラスを使ったオブジェクト指向と比較して柔軟性が高く、動的なオブジェクト生成やクラスの変更が容易であるため、Luaのようなスクリプト言語に適しています。プロトタイプベースのオブジェクト指向
編集Luaはプロトタイプベースのオブジェクト指向言語であり、クラスを定義するのではなく、オブジェクトのプロトタイプを作成することでオブジェクトを生成します。
オブジェクトはテーブルとして表現され、オブジェクトの振る舞いや演算子の振る舞いを定義するために、テーブルにメソッドを追加できます。また、メソッドの呼び出しには「.」を使います。
メタテーブルは、テーブルに特定の動作を定義するために使用されます。例えば、メタテーブルを使用して、オブジェクトが加算、減算、比較などの演算をサポートするようにすることができます。
メタテーブルは、__index、__newindex、__add、__sub、__mul、__div、__mod、__pow、__unm、__eq、__lt、__le、__callなどの特殊なキーを持つテーブルとして定義されます。これらのキーには、特定の動作が関連付けられています。
具体的な例を見てみましょう。以下のようなAnimalテーブルを作成します。
Animal = {sound = "No sound"} function Animal:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function Animal:makeSound() print(self.sound) end
この例では、Animalテーブルにsoundフィールドを定義し、new()関数をオブジェクト作成のために定義しています。makeSound()関数はAnimalオブジェクトが呼び出されたときに、オブジェクトのsoundフィールドの音を出力するために使用されます。
次に、AnimalテーブルからCatテーブルを作成します。
Cat = Animal:new({sound = "Meow"}) function Cat:scratch() print("I am scratching stuff!") end
この例では、CatテーブルはAnimalテーブルから派生しており、CatオブジェクトにはmakeSound()メソッドが含まれ、soundフィールドには"Meow"という値が格納されています。scratch()メソッドも追加されました。
最後に、新しいCatオブジェクトを作成して、メソッドの動作を確認します。
myCat = Cat:new{} myCat:makeSound() -- Prints "Meow" myCat:scratch() -- Prints "I am scratching stuff!"
この例では、Catテーブルを使用することで、AnimalテーブルのmakeSound()メソッドを再利用し、新しいメソッドscratch()を追加しました。また、メタテーブルを通じて、オブジェクトとしてのCatの振る舞いをカスタマイズすることもできます。
複素数オブジェクトの定義
編集ユーザー定義のオブジェクトの例として、複素数オブジェクトの定義を取り上げます。
- complex.lua
-- 複素数オブジェクト Complex = {} function Complex:new(r, i) --[[ Create a new 'Complex' object. @param r The real part of the complex number. @param i The imaginary part of the complex number. ]] local c = {} setmetatable(c, self) self.__index = self c.r = r or 0 c.i = i or 0 return c end -- 複素数の加算 function Complex:__add(other) --[[ Add two 'Complex' objects. @param other The 'Complex' object to add to self. @return A new 'Complex' object representing the sum of self and other. ]] return Complex:new(self.r + other.r, self.i + other.i) end -- 複素数の減算 function Complex:__sub(other) --[[ Subtract two 'Complex' objects. @param other The 'Complex' object to subtract from self. @return A new 'Complex' object representing the difference between self and other. ]] return Complex:new(self.r - other.r, self.i - other.i) end -- 複素数の乗算 function Complex:__mul(other) --[[ Multiply two 'Complex' objects. @param other The 'Complex' object to multiply with self. @return A new 'Complex' object representing the product of self and other. ]] return Complex:new(self.r * other.r - self.i * other.i, self.r * other.i + self.i * other.r) end -- 複素数の除算 function Complex:__div(other) --[[ Divide two 'Complex' objects. @param other The 'Complex' object to divide self by. @return A new 'Complex' object representing the quotient of self and other. ]] local denom = other.r * other.r + other.i * other.i return Complex:new((self.r * other.r + self.i * other.i) / denom, (self.i * other.r - self.r * other.i) / denom) end -- 複素数の絶対値 function Complex:abs() --[[ Get the absolute value of the 'Complex' object. @return A number representing the absolute value of the complex number. ]] return math.sqrt(self.r^2 + self.i^2) end -- 複素数の文字列化 function Complex:__tostring() --[[ Get the string representation of the 'Complex' object. @return A string representing the complex number. ]] if (self.i < 0) then return self.r .. "-" .. -self.i .. "i" end return self.r .. "+" .. self.i .. "i" end -- サンプルコード c1 = Complex:new(1, 2) c2 = Complex:new(3, 4) print(c1) -- 1+2i print(c2) -- 3+4i print(c1 + c2) -- 4+6i print(c1 - c2) -- -2-2i print(c1 * c2) -- -5+10i print(c1 / c2) -- 0.44+0.08i print(c2:abs()) -- 5.0
テンソルオブジェクトの定義
編集-- テンソルオブジェクトを定義するコンストラクタ関数 function Tensor(...) local obj = {} obj.data = {...} obj.size = #obj.data setmetatable(obj, TensorMeta) return obj end -- テンソルオブジェクトに対するメタテーブル TensorMeta = {} -- テンソルオブジェクトの足し算 function TensorMeta.__add(a, b) local c = {} for i = 1, a.size do c[i] = a.data[i] + b.data[i] end return Tensor(table.unpack(c)) end -- テンソルオブジェクトの引き算 function TensorMeta.__sub(a, b) local c = {} for i = 1, a.size do c[i] = a.data[i] - b.data[i] end return Tensor(table.unpack(c)) end -- テンソルオブジェクトの掛け算 function TensorMeta.__mul(a, b) local c = {} for i = 1, a.size do c[i] = a.data[i] * b.data[i] end return Tensor(table.unpack(c)) end -- テンソルオブジェクトの割り算 function TensorMeta.__div(a, b) local c = {} for i = 1, a.size do c[i] = a.data[i] / b.data[i] end return Tensor(table.unpack(c)) end -- テンソルオブジェクトの生成 a = Tensor(1, 2, 3) b = Tensor(4, 5, 6) -- テンソルオブジェクトの足し算 c = a + b print(c.data[1], c.data[2], c.data[3]) --> 5 7 9 -- テンソルオブジェクトの引き算 c = a - b print(c.data[1], c.data[2], c.data[3]) --> -3 -3 -3 -- テンソルオブジェクトの掛け算 c = a * b print(c.data[1], c.data[2], c.data[3]) --> 4 10 18 -- テンソルオブジェクトの割り算 c = a / b print(c.data[1], c.data[2], c.data[3]) --> 0.25 0.4 0.5
LuaRocks
編集LuaRocksは、Lua言語用のパッケージ管理ツールです。 このツールを使用すると、Luaの拡張機能やユーティリティを簡単にインストール、アップグレード、削除することができます。 LuaRocksを使うためには、先にLuaのランタイムが必要になります。
以下に、LuaRocksの基本的な使い方を説明します。
パッケージのインストール
編集新しいパッケージをインストールするには、以下のコマンドを実行します。
$ luarocks install package-name
たとえば、luasocketというパッケージをインストールする場合は、以下のように入力します。
$ luarocks install luasocket
パッケージのアンインストール
編集不要なパッケージをアンインストールするには、以下のコマンドを実行します。
$ luarocks remove package-name
たとえば、先ほどインストールしたluasocketパッケージをアンインストールする場合は、以下のように入力します。
$ luarocks remove luasocket
パッケージの一覧表示
編集インストールされているパッケージの一覧を表示するには、以下のコマンドを入力します。
$ luarocks list
パッケージのバージョン確認
編集インストールされているパッケージのバージョンを確認するには、以下のコマンドを入力します。
$ luarocks show package-name
ただし、このコマンドはインストールされているパッケージに限定されます。インストールされていないパッケージのバージョンを確認したい場合は、以下のように入力します。
$ luarocks search package-name
以上が、LuaRocksの基本的な使い方です。これらのコマンドを実行することで、Luaの拡張機能やユーティリティを簡単にインストール、アップグレード、削除することができます。
Luadoc
編集LuaDocは、Luaのドキュメンテーションツールであり、ソースコード内にある関数やモジュールについての情報を自動的に抽出して、HTML、XML、またはテキスト形式のドキュメントを生成することができます。
以下に、LuaDocによって生成されたドキュメントに含まれる情報の種類を示します。
- モジュールの名前、概要、および説明
- 関数、引数、戻り値、引数と戻り値の種類、および関数の概要と説明
- 変数、その型、および変数の説明
- モジュールとその関数、変数、およびテーブルの階層構造
- 参照関係や関連する関数などのリンク
LuaDocは、Luaのテーブル、オブジェクト指向プログラミング、およびバージョン制御システムにも対応しています。また、LuaDocは、ユーザー定義のタグを使用して、カスタムタグを作成することもできます。
LuaDocには、以下のような主な機能があります。
- ドキュメント化されたコードの抽出:LuaDocは、Luaのソースコードから、コメントと関数/変数の情報を抽出することができます。これらの情報は、モジュールレベルのドキュメント、関数単位のドキュメント、および変数単位のドキュメントとして出力されます。
- 多様な出力形式:LuaDocは、HTML、LaTeX、XML、およびテキスト形式など、多様な形式でドキュメントを出力することができます。出力形式は、テンプレートを使用してカスタマイズすることもできます。
- モジュールや関数のグラフィカルな階層構造:LuaDocは、モジュールや関数の階層構造をドキュメント化し、グラフィカルな形式で表示することができます。
ユーザー定義の拡張 LuaDocは、ユーザーが独自のタグを定義して、ドキュメント化する情報をカスタマイズすることができます。
以上が、LuaDocの概要です。LuaDocは、Luaコミュニティーにおいて、広く使われているドキュメンテーションツールであり、Luaプロジェクトの開発者にとって重要なツールの1つです。
基本構文
編集LuaDocで使用される基本構文の例です。
コメント
編集Luadocは、特殊な方法でLuaのコメントを認識します。次のような例です。
--- This is a comment that LuaDoc recognizes.
---で始まる行は、LuaDocによってドキュメントの一部として解釈されます。コメント行を続けて行うことが可能で、それらは全て同じドキュメントテキストとして処理されます。
--- This is a comment that LuaDoc recognizes. --- It continues onto another line.
コメントには、行に続くタグも含められることがあります。コメントとタグの間には、スペースが必要です。
--- @param x The x coordinate. --- @param y The y coordinate. function point(x,y) -- ... end
タグ
編集LuaDocでは、いくつかのドキュメントタグが使用されます。下記に例を挙げます。
@param
編集関数の引数の説明を提供します。
--- @param x The x coordinate. --- @param y The y coordinate. function point(x,y) -- ... end
@return
編集関数が返す値を説明します。
--- @return A new point table. function point(x,y) -- ... end
@see
編集関数などの関連する内容を参照先を説明するタグ。
--- @see UUIDUtils function generateUUID() -- ... end
このようにして、LuaDocを使用することで、Luaのコードに対してドキュメントを自動的に生成することができます。
基本的な用語
編集- コメント - コード内の説明を記述するためのメモ。行の先頭に
--
を付けることで作成できる。 - タグ - コメントに埋め込まれたマークアップタグ。特定のパターンを持つコメント行に対応するために使用されます。
- ドキュメント - コードから自動生成された説明やリファレンス。
- API - データ構造や関数、その他のプログラミングインターフェイスの全体的なセット。
- プロファイル - プログラムのパフォーマンスを測定すること。
Luadoc タグ
編集Luadocは、コメントに記述された特定のパターンを解析し、ドキュメントの生成に使用します。コメントに挿入されるタグの例をいくつか示します。
@param
- 関数のパラメーターの説明を提供するために使用されます。@return
- 関数の戻り値の説明を提供するために使用されます。@class
- クラスのドキュメントを示すために使用されます。@module
- モジュールのドキュメントを示すために使用されます。@field
- クラスメンバーの説明を提供するために使用します。@see
- 別の場所にある関連ドキュメントへの参照を提供するために使用されます。@tparam
- ジェネリック関数の型パラメーターの説明を提供するために使用されます。
Luadoc コマンド
編集リファレンスの生成、文書のビルド、ソースコードのプロファイリングなど、Luadocのさまざまなコマンドを利用できます。以下はいくつかの代表的なコマンドです。
luadoc
- ソースファイルからドキュメントを生成し、HTML形式で出力します。luadoc -d <directory> <filename>
- 出力フォルダを指定してファイルからドキュメントを生成します。luadoc -h
- ヘルプを表示します。luadoc -p
- ソースコードをプロファイリングしてパフォーマンスに関する情報を生成します。
Luadoc セクション
編集@module <module_name>
- モジュールのドキュメントを作成します。@class <class_name>
- クラスのドキュメントを作成します。@method <method_name>
- クラスメソッドのドキュメントを作成します。@tparam <type>
- ジェネリック関数の型パラメーターのドキュメントを作成します。@property <property_name>
- クラスのメンバープロパティのドキュメントを作成します。
ファイル入出力
編集以下はLuaにおけるファイル入出力に関する基本的な操作の例です。
ファイルの読み込み
編集-- ファイルを開く local file = io.open("sample.txt", "r") -- ファイルから1行ずつ読み込む for line in file:lines() do print(line) end -- ファイルを閉じる file:close()
上記の例では、io.open
関数を使ってファイルを開き、file
変数に代入しています。第一引数にファイル名、第二引数にモードを指定します。r
モードは読み込み専用モードを表します。
次に、file:lines()
を使ってファイルから1行ずつ読み込み、for
ループを使って行を出力しています。
最後に、ファイルを閉じるためにfile:close()
を呼び出しています。
ファイルの書き込み
編集-- ファイルを開く local file = io.open("output.txt", "w") -- ファイルに文字列を書き込む file:write("Hello, world!") -- ファイルを閉じる file:close()
上記の例では、io.open
関数を使ってファイルを開き、file
変数に代入しています。第一引数にファイル名、第二引数にモードを指定します。w
モードは書き込み専用モードを表します。既存のファイルがある場合、内容が上書きされます。
次に、file:write()
を使ってファイルに文字列を書き込んでいます。
最後に、ファイルを閉じるためにfile:close()
を呼び出しています。
上記の例では、ファイルへの書き込みが完了した時点で、ファイルが閉じられるため、ファイルを明示的に閉じる必要はありません。ただし、ファイルを開いたままにしておくと、メモリリークなどの問題が発生する可能性があるため、ファイルの操作が終了したら必ずfile:close()
を呼び出すようにしてください。
モジュールとパッケージ
編集モジュールとは、一連の関数や変数をまとめたもので、再利用性を高めたコードの共有方法です。パッケージは、複数のモジュールをまとめて管理する方法です。
Luaにおいて、モジュールはテーブルを使って実装されます。通常、モジュールは新しいテーブルを作成して、そこに関数や変数を格納します。モジュールを利用する側は、そのモジュールが提供する関数や変数にアクセスするために、モジュールをロードする必要があります。
パッケージは、複数のモジュールを一つのパッケージにまとめ、そのパッケージを利用する方法を提供します。パッケージには、一つ以上のモジュールが含まれている場合があります。通常、パッケージはディレクトリ階層を使って管理され、パッケージの名前はディレクトリ名に対応します。パッケージは、Luaの標準ライブラリからロードすることもできますし、カスタムのパッケージを作成することもできます。
以下は、モジュールとパッケージの例です。
- mymodule.lua
local mymodule = {} function mymodule.greet(name) print("Hello, " .. name .. "!") end return mymodule
- main.lua
local mymodule = require("mymodule") mymodule.greet("Alice")
- mypackage/init.lua
local mypackage = {} mypackage.mymodule = require("mypackage.mymodule") return mypackage
- main.lua
local mypackage = require("mypackage") mypackage.mymodule.greet("Bob")
これらのコードは、"mymodule.lua"という名前のモジュールを作成し、"main.lua"でロードして使用する例と、"mypackage"という名前のパッケージを作成し、その中に"mymodule"という名前のモジュールを含めて、"main.lua"で使用する例を示しています。
Luaの拡張性とC API
編集Luaは、C言語による拡張が容易に行えるように設計されています。LuaのC APIを使うことで、C言語で書かれたモジュールやライブラリをLuaから利用することができます。 LuaのC APIは、Luaの仕様に基づいて作られているため、C言語からLuaスクリプトを実行したり、LuaスクリプトからC言語の関数を呼び出したりすることができます。
Luaの拡張性とは何か?
編集Luaは拡張性に優れたスクリプト言語で、ホストアプリケーションに合わせて柔軟にカスタマイズすることができます。LuaはCで実装されており、C APIを通じてLuaの機能を拡張することができます。
C APIを使用することで、CプログラムからLuaスクリプトを呼び出すことができます。また、LuaスクリプトからC関数を呼び出すこともできます。C APIには、Luaの値のスタックを操作するための関数や、新しい関数やテーブルを作成するための関数などが含まれています。
例えば、以下のようなCプログラムを考えてみましょう。
#include <lua.h> #include <lauxlib.h> #include <lualib.h> int main(int argc, char *argv[]) { lua_State *L = luaL_newstate(); // Luaステートを作成 luaL_openlibs(L); // Luaの標準ライブラリをオープン luaL_dostring(L, "print('Hello, world!')"); // Luaスクリプトを実行 lua_close(L); // Luaステートを解放 return 0; }
このプログラムでは、luaL_newstate
関数でLuaステートを作成し、luaL_openlibs
関数でLuaの標準ライブラリをオープンしています。そして、luaL_dostring
関数でLuaスクリプトを実行しています。最後に、lua_close
関数でLuaステートを解放しています。
このように、C APIを使用することでLuaの機能を拡張することができます。しかし、C APIは複雑であるため、Luaの基礎を理解した上で使用することをおすすめします。
プロジェクト例
編集簡単なプロジェクトの作成
編集以下は、数を受け取り、それが偶数か奇数かを判定する簡単なLuaプログラムの例です。
function even_or_odd(num) if num % 2 == 0 then return "even" else return "odd" end end -- ユーザーから数値を受け取る io.write("Enter a number: ") num = io.read("*n") -- 数字が偶数か奇数か判定する result = even_or_odd(num) -- 結果を表示する print(num .. " is " .. result)
このプログラムでは、 even_or_odd
関数が数値が偶数か奇数かを判定するために使用されています。その後、 io
ライブラリを使用してユーザーから数値を取得し、 even_or_odd
関数を呼び出して結果を表示します。
プロジェクトの改良と拡張
編集次に、このプログラムを改良して、偶数と奇数のカウントを追加してみましょう。
function even_or_odd(num) if num % 2 == 0 then return "even" else return "odd" end end -- カウンターの初期化 even_count = 0 odd_count = 0 -- 数字を 10 個受け取る for i = 1, 10 do io.write("Enter a number: ") num = io.read("*n") result = even_or_odd(num) if result == "even" then even_count = even_count + 1 else odd_count = odd_count + 1 end end -- 結果を表示する print("Even numbers: " .. even_count) print("Odd numbers: " .. odd_count)
このプログラムでは、偶数と奇数のカウントが even_count
と odd_count
変数に格納されます。その後、ループの最後にそれらを表示します。
このプログラムは、より複雑な機能を追加して、Luaプログラムを改良し、拡張する方法を示しています。
附録
編集チートシート
編集-- 変数の宣言と代入 local x = 10 local y = "Hello World!" -- 条件分岐 if x > 5 then print("x is greater than 5") elseif x == 5 then print("x is equal to 5") else print("x is less than 5") end -- ループ処理 for i = 1, 10 do print(i) end -- テーブルの作成と操作 local t = {1, 2, 3, 4, 5} t[6] = 6 table.insert(t, 7) table.remove(t, 1) -- 関数の定義と呼び出し function add(a, b) return a + b end local result = add(3, 5) print(result) -- 8 -- ファイルの読み込みと書き込み local file = io.open("example.txt", "w") file:write("Hello World!") file:close() local file = io.open("example.txt", "r") local content = file:read("*all") print(content) -- Hello World! file:close()
コードギャラリー
編集エラトステネスの篩
編集function eratosthenes(n) sieve = {} for i = 1, n do sieve[i] = true end sieve[1] = false for i = 2, n do if sieve[i] then for j = i * i, n, i do sieve[j] = false end end if i * i >= n then break end end for i = 2, n do if sieve[i] then print(i) end end eratosthenes(100)
このLuaのコードは、エラトステネスの篩を使って与えられた n
までの素数を見つける関数 eratosthenes
を定義しています。
sieve
テーブルはtrue
で初期化され、i
番目の要素が素数であるかどうかを示します。最初のループでsieve[1]
をfalse
に設定しているのは、1は素数ではないためです。- 次のループでは、
2
からn
までの各数値に対して、その数値が素数である場合に以下の操作を行います:- 素数を見つけた場合、その数値を表示します(
print(i)
)。 - その後、その素数の倍数を
false
に設定しています。例えば、2が素数である場合は、2の倍数である4、6、8、10...をfalse
にしています。
- 素数を見つけた場合、その数値を表示します(
このプログラムは eratosthenes(100)
を呼び出すことで、100
までの素数を見つけて表示します。
最大公約数と最小公倍数
編集function reduce(operation, values) local result = values[1] for i = 2, #values do result = operation(result, values[i]) end return result end function gcd2(m, n) return n == 0 and m or gcd2(n, m % n) end function gcd(...) local ints = { ... } if #ints == 0 then error("List of integers cannot be empty") end return reduce(gcd2, ints) end function lcm2(m, n) return m * n / gcd2(m, n) end function lcm(...) local ints = { ... } if #ints == 0 then error("List of integers cannot be empty") end return reduce(lcm2, ints) end print("gcd2(30, 45) =>", gcd2(30, 45)) print("gcd(30, 72, 12) =>", gcd(30, 72, 12)) print("lcm2(30, 72) =>", lcm2(30, 72)) print("lcm(30, 42, 72) =>", lcm(30, 42, 72))
reduce
関数
reduce
関数は、渡されたoperation
関数を使用して、配列の値をリダクション(縮約)します。- 配列の最初の要素を初期値とし、それ以降の要素に対して
operation
を繰り返し適用して最終的な結果を返します。
gcd2
関数
gcd2
関数は、ユークリッドの互除法を使用して整数m
とn
の最大公約数(GCD)を再帰的に計算します。- 三項演算子
n == 0 and m or gcd2(n, m % n)
を使用して、n
が 0 ならばm
を、そうでなければ再帰的にgcd2
を呼び出します。
gcd
関数
gcd
関数は、可変長引数を受け取り、その整数の最大公約数(GCD)を計算します。- 引数がない場合はエラーをスローします。
reduce(gcd2, ints)
を使用して、gcd2
を使って可変長引数内の各整数に対して最大公約数を計算します。
lcm2
関数
lcm2
関数は、整数m
とn
の最小公倍数(LCM)を計算します。m * n / gcd2(m, n)
を使用して、m
とn
の積を最大公約数(gcd2
)で割ってLCMを計算しています。
lcm
関数
lcm
関数は、可変長引数を受け取り、その整数の最小公倍数(LCM)を計算します。- 引数がない場合はエラーをスローします。
reduce(lcm2, ints)
を使用して、lcm2
を使って可変長引数内の各整数に対して最小公倍数を計算します。
- メインプログラム
- メインプログラムでは、それぞれの関数を呼び出して結果を表示しています。
二分法
編集function bisection(low, high, f) local x = (low + high) / 2 local fx = f(x) if math.abs(fx) < 1.0e-10 then return x end if fx < 0.0 then low = x else high = x end return bisection(low, high, f) end local result1 = bisection(0, 3, function(x) return x - 1 end) print(result1) local result2 = bisection(0, 3, function(x) return x * x - 1 end) print(result2)
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Lua に移植しました。
このLuaのコードは、二分法を使って関数の根(方程式の解)を見つけるための関数 bisection
を実装しています。ここで、渡された関数 f
は、与えられた範囲 low
と high
の間で解を探索します。絶対値が非常に小さい(この場合は 1.0e-10
より小さい)値を見つけた場合、その x
の値を解として返します。
次に、与えられた2つの方程式に対して bisection
関数を使用し、それぞれの解を求めています。
result1
は、関数f(x) = x - 1
の解を求めます。result2
は、関数f(x) = x^2 - 1
の解を求めます。
それぞれの解が print
を使ってコンソールに出力されます。このように、bisection
関数を用いて与えられた関数の根を見つけることができます。
用語集
編集- Lua: スクリプト言語であり、拡張性、高速性、軽量性、可読性の高さが特徴である。
- グローバル変数(Global variable): プログラムのどの場所からでも参照可能な変数で、スクリプトの実行中にグローバルテーブルに保存される。
- ローカル変数(Local variable): 宣言されたブロック内でのみ参照可能な変数で、メモリの効率的な使用が可能。
- テーブル(Table): Luaで使用される基本的なデータ構造で、配列や連想配列を実現する。
- モジュール(Module): 1つ以上の関数や変数を含むLuaファイルであり、関連する機能をグループ化することができる。
- コルーチン(Coroutine): 複数のLuaプログラムの実行を同時に行うために使用される機能で、プログラムの実行を中断して別の実行に切り替え、必要なときに再開することができる。
- ガベージコレクション(Garbage collection): Luaにおいて、メモリ管理を自動的に行う仕組みであり、不要になったオブジェクトを自動的に解放することができる。
- メタテーブル(Metatable): テーブルの振る舞いをカスタマイズするために使用される、特別なテーブルである。
- コンパイル(Compile): スクリプト言語のプログラムをバイナリコードに変換することで、実行速度を高めることができる。
- C API: LuaプログラムをC/C++プログラムから操作するためのAPIであり、Luaプログラムの実行やテーブル操作、例外処理などを提供する。