コンテンツにスキップ

Lua/チュートリアル

出典: フリー教科書『ウィキブックス(Wikibooks)』
< 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 StudioVisual Studio Codeなどの、Lua開発に特化したエディターです。 これらのエディターには、デバッグ機能やコード補完機能など、開発を効率化する機能が多数備わっています。

変数とデータ型

[編集]

変数の宣言

[編集]

Luaでは、変数名の前にlocalキーワードを付けることで、その変数をローカルスコープで宣言することができます。例えば、以下のようにして、xという変数を宣言することができます。

local x

変数に初期値を代入する場合は、以下のように宣言と代入を同時に行うことができます。

local x = 42

データ型の種類

[編集]

Luaには、以下のようなデータ型があります。

  • nil: 値が存在しないことを表す
  • boolean: trueまたはfalseの値を表す
  • number: 数値を表す
  • string: 文字列を表す
  • function: 関数を表す
  • table: テーブルを表す(配列や連想配列など、複数の値をまとめて扱うデータ構造)
整数サポートの改善
Lua 5.3以降には整数に関するいくつかの改良が加えられています。

まず、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では型宣言は不要ですか?
はい、Luaでは変数の宣言時に型を明示的に指定する必要はありません。変数に初期値を代入する場合には、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
luaにはcontinue文はないのですか?
Luaには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
このように、Luaでは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には、多くのプログラミング言語と同様に関数があります。

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の関数は柔軟性が高く、多様な用途に対応することができます。

文字列操作

[編集]

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)
テーブルとプロトタイプベースのオブジェクト指向
Luaはプロトタイプベースのオブジェクト指向を採用しているため、テーブルを使ってオブジェクトを表現します。つまり、テーブルを使ってオブジェクトを生成し、そのテーブルを複製して新しいオブジェクトを作成したり、テーブル同士を結合してオブジェクトを拡張することができます。

具体的には、テーブルにメソッドや属性を格納してオブジェクトを表現します。オブジェクトの作成には、テーブルを複製して必要な属性を追加したり、既存のテーブルに属性を追加することで実現できます。

例えば、以下のように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_countodd_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関数は、ユークリッドの互除法を使用して整数 mn の最大公約数(GCD)を再帰的に計算します。
  • 三項演算子 n == 0 and m or gcd2(n, m % n) を使用して、n が 0 ならば m を、そうでなければ再帰的に gcd2 を呼び出します。
gcd 関数
  • gcd関数は、可変長引数を受け取り、その整数の最大公約数(GCD)を計算します。
  • 引数がない場合はエラーをスローします。
  • reduce(gcd2, ints) を使用して、gcd2を使って可変長引数内の各整数に対して最大公約数を計算します。
lcm2 関数
  • lcm2関数は、整数 mn の最小公倍数(LCM)を計算します。
  • m * n / gcd2(m, n) を使用して、mn の積を最大公約数(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 は、与えられた範囲 lowhigh の間で解を探索します。絶対値が非常に小さい(この場合は 1.0e-10 より小さい)値を見つけた場合、その x の値を解として返します。

次に、与えられた2つの方程式に対して bisection 関数を使用し、それぞれの解を求めています。

  • result1 は、関数 f(x) = x - 1 の解を求めます。
  • result2 は、関数 f(x) = x^2 - 1 の解を求めます。

それぞれの解が print を使ってコンソールに出力されます。このように、bisection 関数を用いて与えられた関数の根を見つけることができます。

用語集

[編集]
  1. Lua: スクリプト言語であり、拡張性、高速性、軽量性、可読性の高さが特徴である。
  2. グローバル変数(Global variable): プログラムのどの場所からでも参照可能な変数で、スクリプトの実行中にグローバルテーブルに保存される。
  3. ローカル変数(Local variable): 宣言されたブロック内でのみ参照可能な変数で、メモリの効率的な使用が可能。
  4. テーブル(Table): Luaで使用される基本的なデータ構造で、配列や連想配列を実現する。
  5. モジュール(Module): 1つ以上の関数や変数を含むLuaファイルであり、関連する機能をグループ化することができる。
  6. コルーチン(Coroutine): 複数のLuaプログラムの実行を同時に行うために使用される機能で、プログラムの実行を中断して別の実行に切り替え、必要なときに再開することができる。
  7. ガベージコレクション(Garbage collection): Luaにおいて、メモリ管理を自動的に行う仕組みであり、不要になったオブジェクトを自動的に解放することができる。
  8. メタテーブル(Metatable): テーブルの振る舞いをカスタマイズするために使用される、特別なテーブルである。
  9. コンパイル(Compile): スクリプト言語のプログラムをバイナリコードに変換することで、実行速度を高めることができる。
  10. C API: LuaプログラムをC/C++プログラムから操作するためのAPIであり、Luaプログラムの実行やテーブル操作、例外処理などを提供する。