Lua

#20
TIOBE#30
PYPL#19
GitHub#26
IEEESpectrum#15
JetBrains#17
プログラミング言語組み込みスクリプトゲーム開発軽量拡張

プログラミング言語

Lua

概要

Luaは軽量で高速な組み込み用スクリプト言語で、ゲーム開発、組み込みシステム、設定スクリプトで使用されます。

詳細

Luaは1993年にブラジルの研究グループによって開発された軽量なプログラミング言語です。「Lua」はポルトガル語で「月」を意味します。わずか数十キロバイトの非常に小さなインタープリターでありながら、強力な機能を持つことで知られています。C言語で実装され、C/C++アプリケーションに簡単に組み込むことができるため、主に他のアプリケーションの拡張言語として使用されます。ゲーム業界では特に人気が高く、World of Warcraft、Angry Birds、Robloxなど多くの有名ゲームで使用されています。シンプルで直感的な構文、動的型付け、自動メモリ管理、強力なテーブル機能を特徴とし、小さなフットプリントながら高いパフォーマンスを実現しています。組み込みシステム、設定ファイル、ネットワーク機器のスクリプトなど、幅広い分野で活用されています。

書き方の例

Hello World

-- 基本的な出力
print("Hello, World!")

-- 複数の値を出力
print("Hello,", "Lua!")

-- 変数を使った出力
local message = "こんにちは、Lua!"
print(message)

-- 文字列の連結
local name = "太郎"
local age = 25
print("私の名前は" .. name .. "で、" .. age .. "歳です。")

-- string.format を使った書式設定
local formatted = string.format("私の名前は%sで、%d歳です。", name, age)
print(formatted)

-- 関数を使った例
function greet(name)
    return "こんにちは、" .. name .. "さん!"
end

print(greet("山田"))

-- ローカル関数
local function createMessage(name, age)
    return string.format("名前: %s, 年齢: %d", name, age)
end

print(createMessage("佐藤", 30))

-- 複数行の文字列
local multiline = [[
これは複数行の
文字列です。
Luaでは[[]]で囲みます。
]]
print(multiline)

-- 条件付き出力
local debug = true
if debug then
    print("デバッグモードが有効です")
end

-- ループを使った例
for i = 1, 5 do
    print("Hello " .. i .. "回目!")
end

-- テーブルを使った例
local fruits = {"りんご", "バナナ", "オレンジ"}
for i, fruit in ipairs(fruits) do
    print(i .. "番目の果物: " .. fruit)
end

-- 時刻の表示
print("現在時刻: " .. os.date("%Y-%m-%d %H:%M:%S"))

-- ファイルへの出力例
local function writeToFile()
    local file = io.open("hello.txt", "w")
    if file then
        file:write("Hello, World from Lua!\n")
        file:write("日本語も書けます\n")
        file:close()
        print("ファイルに書き込みました: hello.txt")
    else
        print("ファイルの作成に失敗しました")
    end
end

writeToFile()

-- スタンドアロンスクリプトとしての実行
if arg and arg[0] then
    print("スクリプトファイル: " .. arg[0])
    if arg[1] then
        print("引数1: " .. arg[1])
    end
end

変数とデータ型

-- Luaにはローカル変数とグローバル変数がある
-- localキーワードを使わないとグローバル変数になる

-- 基本的なデータ型
local stringValue = "文字列"
local numberValue = 42
local floatValue = 3.14159
local booleanValue = true
local nilValue = nil

print("=== 基本データ型 ===")
print("文字列:", stringValue, type(stringValue))
print("整数:", numberValue, type(numberValue))
print("浮動小数点:", floatValue, type(floatValue))
print("真偽値:", booleanValue, type(booleanValue))
print("nil値:", nilValue, type(nilValue))

-- Luaでは整数と浮動小数点は区別されない(Lua 5.3以降で区別可能)
local int = 10
local float = 10.0
print("整数か?:", math.type and math.type(int) or "number")
print("浮動小数点か?:", math.type and math.type(float) or "number")

-- 文字列操作
local str1 = "Hello"
local str2 = "World"
local combined = str1 .. " " .. str2  -- 文字列連結
print("連結:", combined)
print("長さ:", #combined)
print("大文字:", string.upper(combined))
print("小文字:", string.lower(combined))
print("部分文字列:", string.sub(combined, 1, 5))
print("置換:", string.gsub(combined, "World", "Lua"))

-- 文字列内の数値変換
local numStr = "123"
local strNum = 456
print("文字列→数値:", tonumber(numStr), type(tonumber(numStr)))
print("数値→文字列:", tostring(strNum), type(tostring(strNum)))

-- エスケープシーケンス
local escaped = "改行\n タブ\t 引用符\" バックスラッシュ\\"
print("エスケープ文字列:")
print(escaped)

-- 長い文字列リテラル
local longString = [[
複数行にわたる
長い文字列を
記述できます。
エスケープ文字は無視されます: \n \t \"
]]
print("長い文字列:", longString)

-- テーブル(Luaの最も重要なデータ構造)
print("\n=== テーブル ===")

-- 配列として使用
local array = {10, 20, 30, 40, 50}
print("配列:")
for i = 1, #array do  -- Luaの配列は1から始まる
    print("  [" .. i .. "] = " .. array[i])
end

-- 辞書(ハッシュテーブル)として使用
local dict = {
    name = "田中太郎",
    age = 25,
    city = "東京",
    isActive = true
}

print("\n辞書:")
for key, value in pairs(dict) do
    print("  " .. key .. " = " .. tostring(value))
end

-- 混合テーブル(配列と辞書の機能を同時に)
local mixed = {
    "最初の要素",  -- [1]
    "2番目の要素",  -- [2]
    name = "混合テーブル",
    count = 2
}

print("\n混合テーブル:")
print("  [1] =", mixed[1])
print("  [2] =", mixed[2])
print("  name =", mixed.name)
print("  count =", mixed.count)

-- ネストしたテーブル
local nested = {
    personal = {
        name = "山田花子",
        age = 30
    },
    work = {
        company = "サンプル会社",
        position = "エンジニア"
    },
    hobbies = {"読書", "映画鑑賞", "プログラミング"}
}

print("\nネストしたテーブル:")
print("  名前:", nested.personal.name)
print("  年齢:", nested.personal.age)
print("  会社:", nested.work.company)
print("  趣味:")
for i, hobby in ipairs(nested.hobbies) do
    print("    " .. i .. ". " .. hobby)
end

-- テーブルの動的操作
local dynamic = {}
dynamic.name = "動的テーブル"
dynamic[1] = "最初の要素"
dynamic["key with spaces"] = "スペースを含むキー"

-- table.insert と table.remove
table.insert(dynamic, "追加された要素")
table.insert(dynamic, 2, "2番目に挿入")

print("\n動的テーブル:")
for k, v in pairs(dynamic) do
    print("  " .. tostring(k) .. " = " .. tostring(v))
end

-- 変数のスコープ
globalVar = "グローバル変数"  -- localなし

do
    local localVar = "ローカル変数"
    print("\nスコープテスト:")
    print("  ブロック内 - グローバル:", globalVar)
    print("  ブロック内 - ローカル:", localVar)
end

print("ブロック外 - グローバル:", globalVar)
-- print("ブロック外 - ローカル:", localVar)  -- エラーになる

-- 複数代入
local a, b, c = 1, 2, 3
local x, y = "Hello", "World"
print("\n複数代入:")
print("a, b, c =", a, b, c)
print("x, y =", x, y)

-- 値の交換
a, b = b, a
print("交換後: a, b =", a, b)

-- 関数から複数の値を返す
local function getNameAge()
    return "佐藤次郎", 35
end

local name, age = getNameAge()
print("関数からの複数戻り値:", name, age)

-- weak参照テーブル(ガベージコレクション関連)
local weak = {}
setmetatable(weak, {__mode = "v"})  -- 値をweak参照に
weak[1] = {data = "テストデータ"}
print("\nWeak参照テーブル作成完了")

関数と制御構造

-- 基本的な関数定義
function add(a, b)
    return a + b
end

-- ローカル関数
local function multiply(a, b)
    return a * b
end

-- 無名関数(関数リテラル)
local subtract = function(a, b)
    return a - b
end

print("=== 基本的な関数 ===")
print("足し算:", add(5, 3))
print("掛け算:", multiply(4, 6))
print("引き算:", subtract(10, 3))

-- 可変引数関数
local function sum(...)
    local args = {...}  -- 可変引数をテーブルに変換
    local total = 0
    for i = 1, #args do
        total = total + args[i]
    end
    return total
end

print("可変引数の合計:", sum(1, 2, 3, 4, 5))

-- select関数を使った可変引数処理
local function printArgs(...)
    print("引数の数:", select("#", ...))
    for i = 1, select("#", ...) do
        print("  引数" .. i .. ":", select(i, ...))
    end
end

print("\n可変引数のテスト:")
printArgs("Hello", "World", 123, true)

-- 複数の戻り値
local function getMinMax(...)
    local args = {...}
    if #args == 0 then return nil, nil end
    
    local min, max = args[1], args[1]
    for i = 2, #args do
        if args[i] < min then min = args[i] end
        if args[i] > max then max = args[i] end
    end
    return min, max
end

local minimum, maximum = getMinMax(5, 2, 8, 1, 9, 3)
print("\n最小値・最大値:", minimum, maximum)

-- クロージャ(関数内関数)
local function createCounter(initial)
    local count = initial or 0
    return function()
        count = count + 1
        return count
    end
end

local counter1 = createCounter(0)
local counter2 = createCounter(100)

print("\nクロージャのテスト:")
print("カウンタ1:", counter1(), counter1(), counter1())  -- 1, 2, 3
print("カウンタ2:", counter2(), counter2())              -- 101, 102

-- 高階関数(関数を引数に取る関数)
local function applyToArray(arr, func)
    local result = {}
    for i, v in ipairs(arr) do
        result[i] = func(v)
    end
    return result
end

local numbers = {1, 2, 3, 4, 5}
local doubled = applyToArray(numbers, function(x) return x * 2 end)
local squared = applyToArray(numbers, function(x) return x * x end)

print("\n高階関数:")
print("元の配列:", table.concat(numbers, ", "))
print("2倍:", table.concat(doubled, ", "))
print("2乗:", table.concat(squared, ", "))

-- 条件分岐
print("\n=== 条件分岐 ===")

local score = 85

if score >= 90 then
    print("優秀")
elseif score >= 80 then
    print("良好")
elseif score >= 70 then
    print("普通")
elseif score >= 60 then
    print("要改善")
else
    print("不合格")
end

-- 三項演算子の代替(and/or演算子)
local grade = score >= 60 and "合格" or "不合格"
print("結果:", grade)

-- ループ構造
print("\n=== ループ構造 ===")

-- 数値forループ
print("数値forループ:")
for i = 1, 5 do
    print("  カウント:", i)
end

print("ステップ付きforループ:")
for i = 10, 2, -2 do  -- 10から2まで-2ずつ
    print("  逆順:", i)
end

-- generic forループ(イテレータ)
local fruits = {"りんご", "バナナ", "オレンジ", "いちご"}

print("ipairs使用(インデックス付き):")
for index, fruit in ipairs(fruits) do
    print("  " .. index .. ": " .. fruit)
end

local person = {name = "田中", age = 30, city = "東京"}

print("pairs使用(キー・値):")
for key, value in pairs(person) do
    print("  " .. key .. " = " .. tostring(value))
end

-- whileループ
print("whileループ:")
local count = 1
while count <= 3 do
    print("  while:", count)
    count = count + 1
end

-- repeatループ(do-while相当)
print("repeatループ:")
count = 1
repeat
    print("  repeat:", count)
    count = count + 1
until count > 3

-- break と 制御構造
print("break文の使用:")
for i = 1, 10 do
    if i == 5 then
        print("  5で中断")
        break
    end
    print("  値:", i)
end

-- goto文(Lua 5.2以降)
print("goto文の例:")
local i = 1
::loop::
if i <= 3 then
    print("  goto:", i)
    i = i + 1
    goto loop
end

-- エラーハンドリング
print("\n=== エラーハンドリング ===")

-- pcall(protected call)を使った安全な関数呼び出し
local function riskyFunction(x)
    if x == 0 then
        error("ゼロは許可されていません")
    end
    return 10 / x
end

local success, result = pcall(riskyFunction, 5)
if success then
    print("成功:", result)
else
    print("エラー:", result)
end

local success, result = pcall(riskyFunction, 0)
if success then
    print("成功:", result)
else
    print("エラー:", result)
end

-- xpcall(extended pcall)でエラーハンドラを指定
local function errorHandler(err)
    return "エラーハンドラで処理: " .. tostring(err)
end

local success, result = xpcall(function() return riskyFunction(0) end, errorHandler)
print("xpcall結果:", success, result)

-- assert関数
local function validateAge(age)
    assert(type(age) == "number", "年齢は数値である必要があります")
    assert(age >= 0 and age <= 150, "年齢は0-150の範囲である必要があります")
    return age
end

local validAge = validateAge(25)
print("有効な年齢:", validAge)

-- 無効な年齢でテスト(コメントアウト)
-- local invalidAge = validateAge("abc")  -- エラーになる

テーブルとメタテーブル

-- Luaの最も強力な機能の一つ:メタテーブル

print("=== テーブルの高度な操作 ===")

-- 基本的なテーブル操作
local array = {10, 20, 30}
print("元の配列:", table.concat(array, ", "))

-- 要素の追加
table.insert(array, 40)        -- 末尾に追加
table.insert(array, 2, 15)     -- 2番目の位置に挿入
print("追加後:", table.concat(array, ", "))

-- 要素の削除
local removed = table.remove(array, 2)  -- 2番目の要素を削除
print("削除された要素:", removed)
print("削除後:", table.concat(array, ", "))

-- ソート
local unsorted = {3, 1, 4, 1, 5, 9, 2, 6}
table.sort(unsorted)
print("ソート後:", table.concat(unsorted, ", "))

-- カスタムソート
local names = {"田中", "佐藤", "鈴木", "高橋"}
table.sort(names, function(a, b) return #a < #b end)  -- 文字数でソート
print("文字数でソート:", table.concat(names, ", "))

-- メタテーブルの基本
print("\n=== メタテーブル ===")

-- 単純なメタテーブル例
local mt = {
    __add = function(a, b)
        return {x = a.x + b.x, y = a.y + b.y}
    end,
    __tostring = function(t)
        return string.format("Point(%d, %d)", t.x, t.y)
    end,
    __index = function(t, key)
        if key == "magnitude" then
            return math.sqrt(t.x^2 + t.y^2)
        end
        return nil
    end
}

local point1 = {x = 3, y = 4}
local point2 = {x = 1, y = 2}

setmetatable(point1, mt)
setmetatable(point2, mt)

-- メタメソッドの使用
local point3 = point1 + point2  -- __add メタメソッド
print("点の加算:", tostring(point3))  -- __tostring メタメソッド
print("point1の大きさ:", point1.magnitude)  -- __index メタメソッド

-- クラスライクなオブジェクト
print("\n=== オブジェクト指向プログラミング ===")

-- Personクラスの定義
local Person = {}
Person.__index = Person

function Person:new(name, age)
    local obj = {
        name = name or "無名",
        age = age or 0
    }
    setmetatable(obj, self)
    return obj
end

function Person:introduce()
    return string.format("私の名前は%sで、%d歳です。", self.name, self.age)
end

function Person:haveBirthday()
    self.age = self.age + 1
    print(self.name .. "さん、" .. self.age .. "歳のお誕生日おめでとう!")
end

function Person:__tostring()
    return string.format("Person{name='%s', age=%d}", self.name, self.age)
end

-- オブジェクトの作成と使用
local person1 = Person:new("田中太郎", 25)
local person2 = Person:new("山田花子", 30)

print("person1:", person1:introduce())
print("person2:", person2:introduce())

person1:haveBirthday()
print("誕生日後:", tostring(person1))

-- 継承の例
local Student = {}
Student.__index = Student
setmetatable(Student, Person)  -- PersonからStudent への継承

function Student:new(name, age, school)
    local obj = Person.new(self, name, age)
    obj.school = school or "未設定"
    setmetatable(obj, self)
    return obj
end

function Student:introduce()
    local base = Person.introduce(self)
    return base .. " " .. self.school .. "に通っています。"
end

function Student:study(subject)
    print(self.name .. "は" .. subject .. "を勉強しています。")
end

local student = Student:new("佐藤一郎", 20, "東京大学")
print("学生の紹介:", student:introduce())
student:study("数学")

-- プロキシテーブル(プロパティアクセスの制御)
print("\n=== プロキシテーブル ===")

local function createReadOnlyTable(t)
    local proxy = {}
    local mt = {
        __index = t,
        __newindex = function(table, key, value)
            error("読み取り専用テーブルへの書き込みは禁止されています")
        end
    }
    setmetatable(proxy, mt)
    return proxy
end

local config = {
    version = "1.0",
    author = "開発者",
    debug = true
}

local readOnlyConfig = createReadOnlyTable(config)
print("バージョン:", readOnlyConfig.version)
print("作者:", readOnlyConfig.author)

-- 書き込みテスト(エラーになる)
-- readOnlyConfig.version = "2.0"  -- エラー

-- プロパティアクセスの監視
local function createWatchedTable()
    local data = {}
    local watchers = {}
    
    local mt = {
        __index = data,
        __newindex = function(t, key, value)
            local oldValue = data[key]
            data[key] = value
            
            -- ウォッチャーに通知
            for _, watcher in ipairs(watchers) do
                watcher(key, oldValue, value)
            end
        end
    }
    
    local proxy = {}
    setmetatable(proxy, mt)
    
    -- ウォッチャー追加メソッド
    proxy.addWatcher = function(watcher)
        table.insert(watchers, watcher)
    end
    
    return proxy
end

local watched = createWatchedTable()
watched.addWatcher(function(key, oldVal, newVal)
    print(string.format("プロパティ変更: %s = %s → %s", 
          key, tostring(oldVal), tostring(newVal)))
end)

watched.name = "テスト"
watched.count = 1
watched.count = 2

モジュールとパッケージ

-- Luaでのモジュールシステム

print("=== モジュールの基本 ===")

-- シンプルなモジュール作成例
local mymath = {}

function mymath.add(a, b)
    return a + b
end

function mymath.multiply(a, b)
    return a * b
end

mymath.pi = 3.14159

-- プライベート関数(モジュール外からアクセス不可)
local function privateHelper(x)
    return x * 2
end

function mymath.doubleAndAdd(a, b)
    return privateHelper(a) + b
end

-- モジュールの使用
print("mymath.add(5, 3):", mymath.add(5, 3))
print("mymath.multiply(4, 6):", mymath.multiply(4, 6))
print("mymath.pi:", mymath.pi)
print("mymath.doubleAndAdd(5, 3):", mymath.doubleAndAdd(5, 3))

-- より高度なモジュールパターン
print("\n=== 高度なモジュールパターン ===")

-- 名前空間を持つモジュール
local utils = {}

utils.string = {}
utils.table = {}
utils.math = {}

-- 文字列ユーティリティ
function utils.string.trim(s)
    return s:gsub("^%s*(.-)%s*$", "%1")
end

function utils.string.split(str, delimiter)
    local result = {}
    local pattern = string.format("([^%s]+)", delimiter)
    for match in str:gmatch(pattern) do
        table.insert(result, match)
    end
    return result
end

-- テーブルユーティリティ
function utils.table.deepCopy(orig)
    local copy
    if type(orig) == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[utils.table.deepCopy(orig_key)] = utils.table.deepCopy(orig_value)
        end
        setmetatable(copy, utils.table.deepCopy(getmetatable(orig)))
    else
        copy = orig
    end
    return copy
end

function utils.table.isEmpty(t)
    return next(t) == nil
end

-- 数学ユーティリティ
function utils.math.clamp(value, min, max)
    return math.max(min, math.min(max, value))
end

function utils.math.lerp(a, b, t)
    return a + (b - a) * t
end

-- ユーティリティの使用例
print("文字列のトリム:", "'" .. utils.string.trim("  hello world  ") .. "'")

local parts = utils.string.split("apple,banana,orange", ",")
print("文字列分割:")
for i, part in ipairs(parts) do
    print("  " .. i .. ": " .. part)
end

local original = {a = 1, b = {c = 2, d = 3}}
local copied = utils.table.deepCopy(original)
copied.b.c = 999
print("元のテーブル b.c:", original.b.c)  -- 2(変更されない)
print("コピーのテーブル b.c:", copied.b.c)  -- 999

print("clamp(15, 0, 10):", utils.math.clamp(15, 0, 10))
print("lerp(0, 100, 0.5):", utils.math.lerp(0, 100, 0.5))

-- ファクトリパターン
print("\n=== ファクトリパターン ===")

local function createLogger(name)
    local logger = {}
    
    function logger.info(message)
        print(string.format("[INFO][%s] %s", name, message))
    end
    
    function logger.warn(message)
        print(string.format("[WARN][%s] %s", name, message))
    end
    
    function logger.error(message)
        print(string.format("[ERROR][%s] %s", name, message))
    end
    
    return logger
end

local appLogger = createLogger("App")
local dbLogger = createLogger("Database")

appLogger.info("アプリケーション開始")
dbLogger.warn("接続が遅いです")
appLogger.error("予期しないエラー")

-- シングルトンパターン
print("\n=== シングルトンパターン ===")

local ConfigManager = {}
local instance = nil

function ConfigManager.getInstance()
    if not instance then
        instance = {
            settings = {},
            
            set = function(self, key, value)
                self.settings[key] = value
            end,
            
            get = function(self, key, default)
                return self.settings[key] or default
            end,
            
            dump = function(self)
                print("設定内容:")
                for k, v in pairs(self.settings) do
                    print("  " .. k .. " = " .. tostring(v))
                end
            end
        }
    end
    return instance
end

-- シングルトンの使用
local config1 = ConfigManager.getInstance()
local config2 = ConfigManager.getInstance()

config1:set("debug", true)
config2:set("version", "1.0")

print("config1とconfig2は同じインスタンス?", config1 == config2)
config1:dump()

-- パッケージローダーのシミュレーション
print("\n=== パッケージシステムのシミュレーション ===")

local packageSystem = {}
packageSystem.loaded = {}

function packageSystem.require(name)
    if packageSystem.loaded[name] then
        return packageSystem.loaded[name]
    end
    
    -- 実際のシステムではファイルから読み込む
    local module
    if name == "json" then
        module = {
            encode = function(t) return "JSON encoded: " .. tostring(t) end,
            decode = function(s) return {decoded = s} end
        }
    elseif name == "http" then
        module = {
            get = function(url) return "HTTP GET: " .. url end,
            post = function(url, data) return "HTTP POST to " .. url end
        }
    else
        error("モジュール '" .. name .. "' が見つかりません")
    end
    
    packageSystem.loaded[name] = module
    return module
end

-- パッケージの使用
local json = packageSystem.require("json")
local http = packageSystem.require("http")

print("JSONエンコード:", json.encode({test = "data"}))
print("HTTP GET:", http.get("http://example.com"))

-- 2回目の require は既にロードされたものを返す
local json2 = packageSystem.require("json")
print("同じインスタンス?", json == json2)

ファイル操作とC言語連携

-- ファイル入出力操作

print("=== ファイル入出力 ===")

-- ファイルへの書き込み
local function writeToFile()
    local filename = "test.txt"
    local file = io.open(filename, "w")
    
    if file then
        file:write("Hello, Lua!\n")
        file:write("これはテストファイルです。\n")
        file:write("現在時刻: " .. os.date("%Y-%m-%d %H:%M:%S") .. "\n")
        
        -- 数値データの書き込み
        for i = 1, 5 do
            file:write(string.format("数値 %d: %d\n", i, i * i))
        end
        
        file:close()
        print("ファイルに書き込み完了: " .. filename)
        return true
    else
        print("ファイルの作成に失敗しました")
        return false
    end
end

-- ファイルからの読み込み
local function readFromFile()
    local filename = "test.txt"
    local file = io.open(filename, "r")
    
    if file then
        print("\nファイル内容:")
        print("=" .. string.rep("=", 30))
        
        -- 全体を一度に読み込み
        local content = file:read("*all")
        file:close()
        print(content)
        print("=" .. string.rep("=", 30))
        return true
    else
        print("ファイルの読み込みに失敗しました")
        return false
    end
end

-- 行ごとの読み込み
local function readLineByLine()
    local filename = "test.txt"
    local file = io.open(filename, "r")
    
    if file then
        print("\n行ごとの読み込み:")
        local lineNumber = 1
        for line in file:lines() do
            print(string.format("行%d: %s", lineNumber, line))
            lineNumber = lineNumber + 1
        end
        file:close()
        return true
    else
        print("ファイルの読み込みに失敗しました")
        return false
    end
end

-- バイナリファイル操作
local function binaryFileOperations()
    local filename = "binary.dat"
    
    -- バイナリ書き込み
    local file = io.open(filename, "wb")
    if file then
        -- いくつかのバイト値を書き込み
        for i = 0, 255, 17 do
            file:write(string.char(i))
        end
        file:close()
        print("バイナリファイル作成完了: " .. filename)
    end
    
    -- バイナリ読み込み
    file = io.open(filename, "rb")
    if file then
        print("バイナリファイル内容:")
        local byte = file:read(1)
        local position = 1
        while byte do
            print(string.format("位置%d: %d (0x%02X)", position, string.byte(byte), string.byte(byte)))
            byte = file:read(1)
            position = position + 1
        end
        file:close()
    end
end

-- CSV ファイル操作
local function csvOperations()
    local csvFile = "data.csv"
    
    -- CSVファイル作成
    local file = io.open(csvFile, "w")
    if file then
        file:write("名前,年齢,部署,給与\n")
        file:write("田中太郎,25,営業部,300000\n")
        file:write("佐藤花子,30,開発部,400000\n")
        file:write("山田一郎,35,管理部,350000\n")
        file:close()
        print("CSVファイル作成完了: " .. csvFile)
    end
    
    -- CSV読み込みと解析
    file = io.open(csvFile, "r")
    if file then
        print("\nCSVデータ:")
        local header = file:read("*line")
        print("ヘッダー: " .. header)
        
        local rowNumber = 1
        for line in file:lines() do
            local fields = {}
            for field in line:gmatch("([^,]+)") do
                table.insert(fields, field)
            end
            
            print(string.format("行%d: 名前=%s, 年齢=%s, 部署=%s, 給与=%s円", 
                  rowNumber, fields[1], fields[2], fields[3], fields[4]))
            rowNumber = rowNumber + 1
        end
        file:close()
    end
end

-- ファイル操作の実行
if writeToFile() then
    readFromFile()
    readLineByLine()
end

binaryFileOperations()
csvOperations()

-- システム情報とOS操作
print("\n=== システム情報 ===")

print("OS時刻:", os.date("%Y-%m-%d %H:%M:%S"))
print("UTC時刻:", os.date("!%Y-%m-%d %H:%M:%S"))
print("エポック時間:", os.time())

-- 日付計算
local birthday = os.time{year=1990, month=5, day=15}
local now = os.time()
local ageInSeconds = now - birthday
local ageInDays = math.floor(ageInSeconds / (24 * 60 * 60))
print("生年月日から今日まで:", ageInDays .. "日")

-- 環境変数(存在する場合)
local home = os.getenv("HOME") or os.getenv("USERPROFILE")
if home then
    print("ホームディレクトリ:", home)
end

local path = os.getenv("PATH")
if path then
    print("PATH環境変数の長さ:", #path .. "文字")
end

-- C言語ライブラリ関数の使用例
print("\n=== C言語ライブラリ関数 ===")

-- 数学ライブラリ
print("数学関数:")
print("sin(π/2):", math.sin(math.pi / 2))
print("cos(0):", math.cos(0))
print("sqrt(16):", math.sqrt(16))
print("log(math.e):", math.log(math.e))
print("random(1,10):", math.random(1, 10))

-- 乱数のシード設定
math.randomseed(os.time())
print("ランダム配列:")
local randomNumbers = {}
for i = 1, 5 do
    randomNumbers[i] = math.random(1, 100)
end
print("  " .. table.concat(randomNumbers, ", "))

-- 文字列ライブラリ
print("\n文字列関数:")
local testString = "Hello, Lua Programming!"
print("元の文字列:", testString)
print("大文字:", string.upper(testString))
print("小文字:", string.lower(testString))
print("文字列の長さ:", string.len(testString))
print("部分文字列(8-10):", string.sub(testString, 8, 10))

-- パターンマッチング
local email = "[email protected]"
local username, domain = string.match(email, "([^@]+)@([^@]+)")
print("メール解析:")
print("  ユーザー名:", username)
print("  ドメイン:", domain)

-- 文字列置換
local text = "今日は晴れです。明日も晴れでしょう。"
local replaced = string.gsub(text, "晴れ", "雨")
print("置換前:", text)
print("置換後:", replaced)

-- テーブルライブラリ
print("\nテーブル関数:")
local fruits = {"りんご", "バナナ", "オレンジ", "いちご"}
print("元の配列:", table.concat(fruits, ", "))

table.sort(fruits)
print("ソート後:", table.concat(fruits, ", "))

local numbers = {3, 1, 4, 1, 5, 9, 2, 6}
table.sort(numbers, function(a, b) return a > b end)  -- 降順
print("降順ソート:", table.concat(numbers, ", "))

-- 簡単なC言語連携のシミュレーション(実際にはC言語側の実装が必要)
print("\n=== C言語連携の概念 ===")
print("実際のC言語連携では以下のような関数を定義できます:")
print("- C言語で高速な計算関数を実装")
print("- LuaからC関数を呼び出し")
print("- C言語からLuaスクリプトを実行")
print("- 共有ライブラリとしてC関数を提供")

-- Luaの限界をCで補完する例の説明
local function simulateCExtension()
    print("\nC拡張の典型的な用途:")
    print("1. 数値計算の高速化")
    print("2. システムAPIへのアクセス")
    print("3. 既存Cライブラリの活用")
    print("4. メモリ集約的な処理")
    print("5. リアルタイム処理")
end

simulateCExtension()

メリット・デメリット

メリット

  • 軽量性: 非常に小さなメモリフットプリント(数十KB)
  • 高いパフォーマンス: コンパクトながら高速実行
  • 組み込み容易: C/C++アプリケーションへの組み込みが簡単
  • シンプルな構文: 学習しやすく読みやすいコード
  • 柔軟なデータ構造: テーブルによる配列・辞書・オブジェクトの統一
  • メタプログラミング: メタテーブルによる強力な機能拡張

デメリット

  • 限られたエコシステム: 標準ライブラリが最小限
  • コミュニティサイズ: 他の主要言語と比較して小規模
  • デバッグツール不足: 開発ツールやIDEサポートが限定的
  • 1から始まる配列: 多くの言語と異なる配列インデックス
  • グローバル変数問題: デフォルトでグローバルスコープになりやすい
  • エラーメッセージ: 実行時エラーの詳細情報が限定的

主要リンク

公式ドキュメント

学習リソース

開発ツール・ライブラリ

活用事例・フレームワーク