SeokguKim's Garage

A little space for SeokguKim


Lua 내용 정리

Tags: development, lua

알고 있는 중요한 내용 정리

Lua는 가볍고 확장 가능한 스크립트 언어로, 주로 임베디드 시스템이나 게임 개발에 많이 사용됩니다. C 언어와의 통합이 용이하며, 배우기 쉬운 문법을 가지고 있습니다.

Lua

Lua는 동적 타입 언어이며, 절차적 프로그래밍, 객체 지향 프로그래밍, 함수형 프로그래밍, 데이터 기술 프로그래밍 등 다양한 프로그래밍 패러다임을 지원합니다. 주요 특징은 다음과 같습니다:

더 읽어보기: Lua 공식 웹사이트

기본 문법

주석


-- 한 줄 주석

  

--[[

여러 줄 주석

입니다.

--]]

변수

Lua의 변수는 기본적으로 전역 변수입니다. 지역 변수를 사용하려면 local 키워드를 사용해야 합니다.


-- 전역 변수

globalVar = 10

  

-- 지역 변수

local localVar = "Hello, Lua!"

  

print(globalVar)

print(localVar)

데이터 타입

Lua는 다음과 같은 기본 데이터 타입을 가지고 있습니다:


local a = nil

local b = true

local c = 10.5

local d = "Lua"

local e = function() print("function") end

local f = {} -- 빈 테이블

  

print(type(a)) -- nil

print(type(b)) -- boolean

print(type(c)) -- number

print(type(d)) -- string

print(type(e)) -- function

print(type(f)) -- table

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#2.1

연산자


local x = 10

local y = 4

  

print(x + y)  -- 14

print(x / y)  -- 2.5

print(x // y) -- 2 (Lua 5.3+)

print("Hello" .. " " .. "World") -- Hello World

  

local condition1 = true

local condition2 = false

print(condition1 and condition2) -- false

print(condition1 or condition2)  -- true

print(not condition1)          -- false

제어 구조

조건문 (if-then-else)


local score = 85

  

if score >= 90 then

    print("A")

elseif score >= 80 then

    print("B")

elseif score >= 70 then

    print("C")

else

    print("F")

end

반복문 (while, repeat-until, for)


-- while 루프

local i = 1

while i <= 5 do

    print("while: " .. i)

    i = i + 1

end

  

-- repeat-until 루프 (조건이 참이 될 때까지 반복)

local j = 1

repeat

    print("repeat: " .. j)

    j = j + 1

until j > 5

  

-- 숫자형 for 루프

for k = 1, 5 do -- 1부터 5까지 1씩 증가 (기본 증가값은 1)

    print("numeric for: " .. k)

end

  

for k = 5, 1, -1 do -- 5부터 1까지 1씩 감소

    print("numeric for (reverse): " .. k)

end

  

-- 제네릭 for 루프 (주로 테이블 순회에 사용)

local myTable = {apple = 1, banana = 2, orange = 3}

for key, value in pairs(myTable) do

    print("generic for (pairs): " .. key .. " = " .. value)

end

  

local myArray = {"a", "b", "c"}

for index, value in ipairs(myArray) do -- ipairs는 정수 인덱스 순서대로 순회

    print("generic for (ipairs): " .. index .. " = " .. value)

end

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#3.3

함수

함수는 일급 값(first-class value)으로, 변수에 할당하거나 다른 함수의 인자로 전달하거나 반환 값으로 사용할 수 있습니다.


-- 함수 정의

function add(a, b)

    return a + b

end

  

local result = add(3, 5)

print(result) -- 8

  

-- 익명 함수

local multiply = function(a, b)

    return a * b

end

print(multiply(3, 5)) -- 15

  

-- 가변 인자 함수

function sum(...)

    local total = 0

    for _, v in ipairs({...}) do -- ...은 가변 인자를 테이블로 만듦

        total = total + v

    end

    return total

end

print(sum(1, 2, 3, 4, 5)) -- 15

  

-- 다중 반환 값

function getCoordinates()

    return 10, 20

end

  

local x, y = getCoordinates()

print(x, y) -- 10   20

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#3.4

테이블 (Tables)

테이블은 Lua의 유일한 데이터 구조화 메커니즘입니다. 배열, 딕셔너리(해시맵), 객체 등으로 사용될 수 있습니다.


-- 빈 테이블 생성

local t1 = {}

  

-- 배열 스타일

local arr = {10, 20, 30, "hello"}

print(arr[1]) -- 10 (Lua 배열 인덱스는 1부터 시작)

print(arr[4]) -- hello

arr[5] = 40

print(#arr) -- 5 (배열 길이 연산자 #)

  

-- 딕셔너리 스타일 (키-값 쌍)

local dict = {

    name = "Lua",

    version = 5.4,

    ["is good"] = true -- 키에 공백이나 특수문자가 있으면 대괄호와 따옴표 사용

}

print(dict.name)       -- Lua

print(dict["version"]) -- 5.4

print(dict["is good"]) -- true

  

dict.year = 2020

print(dict.year) -- 2020

  

-- 테이블 순회

for k, v in pairs(dict) do

    print(k .. ": " .. v)

end

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#2.5.7

메타테이블과 메타메소드 (Metatables and Metamethods)

메타테이블을 사용하여 테이블의 동작을 변경할 수 있습니다. 예를 들어, 두 테이블을 더하는 연산(+)을 정의할 수 있습니다.

메타테이블은 일반 테이블이며, 특정 이벤트(예: __add는 덧셈)에 대한 메타메소드를 가집니다.


local myTable1 = {10, 20}

local myTable2 = {30, 40}

  

local myMetatable = {

    __add = function(t1, t2)

        local resultTable = {}

        for i = 1, math.max(#t1, #t2) do

            resultTable[i] = (t1[i] or 0) + (t2[i] or 0)

        end

        return resultTable

    end,

    __tostring = function(t)

        local s = "{"

        for i, v in ipairs(t) do

            s = s .. v .. (i == #t and "" or ", ")

        end

        return s .. "}"

    end

}

  

setmetatable(myTable1, myMetatable)

-- myTable2는 myTable1과 같은 메타테이블을 공유하거나 자신만의 메타테이블을 가질 수 있습니다.

-- 여기서는 myTable1의 메타테이블이 __add 연산 시 사용됩니다.

  

local sumTable = myTable1 + myTable2 -- __add 메타메소드 호출

print(sumTable) -- {40, 60} (myMetatable의 __tostring 덕분에 이렇게 출력됨)

  

-- __index 메타메소드: 테이블에 없는 키에 접근할 때 호출

local defaults = { x = 0, y = 0 }

local point = { x = 10 }

setmetatable(point, { __index = defaults })

  

print(point.x) -- 10 (point 테이블에 존재)

print(point.y) -- 0  (point 테이블에 없으므로 defaults 테이블에서 찾음)

print(point.z) -- nil (defaults 테이블에도 없음)

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#2.4

모듈과 require

Lua는 require 함수를 사용하여 모듈을 로드합니다. 모듈은 일반적으로 테이블을 반환합니다.


-- mymodule.lua 파일 내용

local M = {}

  

function M.sayHello(name)

    return "Hello, " .. name .. "!"

end

  

return M

  

-- 메인 파일에서 모듈 사용

local myModule = require("mymodule") -- 파일 확장자 .lua는 생략

print(myModule.sayHello("Lua User")) -- Hello, Lua User!

require는 모듈을 한 번만 로드하고 그 결과를 캐시합니다.

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#pdf-require

코루틴 (Coroutines)

코루틴은 협력적 멀티태스킹을 위한 기능입니다. 여러 실행 흐름을 가질 수 있으며, coroutine.yield()를 통해 실행을 중단하고, coroutine.resume()을 통해 재개할 수 있습니다.


local co = coroutine.create(function(a, b)

    print("co-body", a, b)

    local r1 = coroutine.yield(a + b)

    print("co-body", r1)

    local r2 = coroutine.yield(a - b)

    print("co-body", r2)

    return "finished"

end)

  

print("main", coroutine.status(co)) -- main suspended

  

local status, res = coroutine.resume(co, 10, 20) -- 첫 번째 resume, 인자 전달

print("main", status, res) -- main  true    30

  

status, res = coroutine.resume(co, "hello") -- yield에 값 전달

print("main", status, res) -- main  true    -10

  

status, res = coroutine.resume(co, "world")

print("main", status, res) -- main  true    finished

  

status, res = coroutine.resume(co) -- 이미 종료된 코루틴 재개 시도

print("main", status, res) -- main  false   cannot resume dead coroutine

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#2.6

에러 처리 (pcall과 xpcall)

pcall (protected call)은 함수를 안전 모드에서 실행하여, 실행 중 오류가 발생해도 프로그램이 중단되지 않고 오류를 잡아낼 수 있게 합니다.

xpcall은 pcall과 유사하지만, 오류 발생 시 호출될 오류 처리 함수를 지정할 수 있습니다.


function mightFail(arg)

    if type(arg) ~= "number" then

        error("Expected a number, got " .. type(arg), 2) -- 2는 에러 메시지에 보고될 스택 레벨

    end

    return arg * 2

end

  

-- pcall 사용

local status, resultOrError = pcall(mightFail, 10)

if status then

    print("Success:", resultOrError) -- Success:    20

else

    print("Error:", resultOrError)

end

  

status, resultOrError = pcall(mightFail, "text")

if status then

    print("Success:", resultOrError)

else

    print("Error:", resultOrError) -- Error:    [string "--[간단한 Lua 스크립트]..."]:2: Expected a number, got string

end

  

-- xpcall 사용

local function errorHandler(err)

    print("Custom error handler:", err)

    return "Error handled gracefully" -- xpcall의 반환값이 됨

end

  

status, resultOrError = xpcall(mightFail, errorHandler, "another text")

if status then

    print("xpcall Success:", resultOrError)

else

    print("xpcall Handled Error:", resultOrError) -- xpcall Handled Error:  Error handled gracefully

end

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#pdf-pcall, https://www.lua.org/manual/5.4/manual.html#pdf-xpcall

가비지 컬렉션 (Garbage Collection)

Lua는 자동 메모리 관리를 위해 증분형 마크 앤 스윕(incremental mark-and-sweep) 가비지 컬렉터를 사용합니다. 개발자가 직접 메모리를 할당하거나 해제할 필요는 거의 없습니다.

collectgarbage() 함수를 통해 가비지 컬렉터를 제어할 수 있지만, 일반적으로는 필요하지 않습니다.

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#2.5

Lua C API

Lua는 C/C++와 쉽게 통합될 수 있도록 강력한 C API를 제공합니다. 이를 통해 다음이 가능합니다:

이는 Lua가 임베디드 스크립팅 언어로서 강력한 이유 중 하나입니다.

더 읽어보기: https://www.lua.org/manual/5.4/manual.html#4