0Pricing
Lua Academy · 강의

산술 메타메서드

메타메서드로 +,-,*,/ 및 기타 연산자의 동작을 재정의합니다.

산술 메타메서드은(는) CoddyKit의 무료 Lua Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Lua Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

메타메서드 이름

Lua는 모든 산술 연산자에 대한 메타메서드를 정의합니다. Lua가 테이블에 대한 산술 연산을 발견하면 테이블의 메타테이블에서 해당 메타메서드를 찾습니다. 두 피연산자가 모두 테이블이면 Lua는 먼저 왼쪽 피연산자의 메타테이블을 확인합니다.

-- Operator -> Metamethod
-- a + b    -> __add
-- a - b    -> __sub
-- a * b    -> __mul
-- a / b    -> __div
-- a % b    -> __mod
-- a ^ b    -> __pow
-- a // b   -> __idiv
-- -a       -> __unm (unary minus)
-- #a       -> __len
print("Metamethods defined for all Lua operators")

벡터 덧셈을 위한 __add

벡터 테이블에 __add를 정의하면 +를 사용할 수 있습니다. 메타메서드는 두 피연산자를 받아 새 값을 반환합니다. 두 피연산자가 모두 테이블일 수도 있고 하나가 숫자일 수도 있으므로, 메타메서드가 두 경우를 모두 처리해야 합니다.

local Vec = {}
Vec.__index = Vec

Vec.__add = function(a, b)
  return setmetatable({x=a.x+b.x, y=a.y+b.y}, Vec)
end

Vec.__tostring = function(v)
  return string.format("Vec(%g,%g)", v.x, v.y)
end

function Vec.new(x,y) return setmetatable({x=x,y=y},Vec) end

local v1 = Vec.new(1,2)
local v2 = Vec.new(3,4)
print(tostring(v1 + v2))   -- Vec(4,6)

__sub __mul __div

__sub, __mul, __div도 같은 방식으로 정의하십시오. 곱셈에서는 메타메서드 내부에서 피연산자의 형식을 확인하여 table*table과 table*number 경우를 모두 처리하십시오.

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__sub = function(a,b) return V(a.x-b.x, a.y-b.y) end
Vec.__mul = function(a,b)
  if type(a)=="number" then return V(a*b.x, a*b.y)
  elseif type(b)=="number" then return V(a.x*b, a.y*b)
  else return a.x*b.x + a.y*b.y  -- dot product
  end
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end

local v = V(3,4)
print(tostring(v * 2))    -- (6,8)
print(tostring(V(1,0) - V(0,1)))  -- (1,-1)

__unm 단항 마이너스

__unm은 Lua가 -obj를 계산할 때 호출되는 단항 마이너스 메타메서드입니다. 피연산자를 받아(일관성을 위해 같은 값이 두 번 전달됩니다) 음수로 바꾼 값을 반환합니다.

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__unm = function(a) return V(-a.x, -a.y) end
Vec.__tostring = function(v)
  return string.format("(%g,%g)", v.x, v.y)
end

local v = V(3, -4)
print(tostring(-v))    -- (-3,4)
print(tostring(-(-v))) -- (3,-4)

사용자 지정 길이를 위한 __len

정수로 이루어진 연속 구간이 원시 형태로 없는 테이블에서 #obj를 계산하면 __len이 호출됩니다. 집합, 큐 또는 가중치 컨테이너 같은 사용자 지정 형식에 의미 있는 "길이"를 반환하도록 정의하십시오.

local Bag = {}
Bag.__index = Bag
Bag.__len = function(b) return b._count end

function Bag.new()
  return setmetatable({_count=0, _items={}}, Bag)
end
function Bag:add(item, qty)
  self._items[item] = (self._items[item] or 0) + (qty or 1)
  self._count = self._count + (qty or 1)
end

local bag = Bag.new()
bag:add("apple", 3)
bag:add("banana", 2)
print(#bag)   -- 5

__mod와 __pow

__mod는 % 연산자를 처리하고, __pow는 ^ 연산자를 처리합니다. 자주 사용되지는 않지만 큰 정수, 분수 또는 모듈러 산술 객체 같은 사용자 지정 숫자 형식에 유용합니다.

local Modular = {}
Modular.__index = Modular
local M = 1000000007

local function mod(n)
  return setmetatable({n = n % M}, Modular)
end

Modular.__add = function(a,b) return mod(a.n + b.n) end
Modular.__mul = function(a,b) return mod(a.n * b.n) end
Modular.__pow = function(a,b)
  local r, base, exp = mod(1), a, b
  while exp > 0 do
    if exp%2==1 then r = r*base end
    base = base*base; exp = math.floor(exp/2)
  end
  return r
end

print((mod(2) ^ 30).n)   -- 73741817 (2^30 mod 1e9+7)

__concat

적어도 하나의 피연산자가 이 메타메서드를 가지고 있으면 __concat이 .. 연산자에 의해 호출됩니다. 이를 통해 사용자 지정 형식의 연결을 정의하거나 유창한 DSL을 만들 수 있습니다.

local Builder = {}
Builder.__index = Builder
Builder.__concat = function(a, b)
  local result = {}
  for _, v in ipairs(a._parts) do result[#result+1] = v end
  if type(b) == "string" then result[#result+1] = b
  else for _, v in ipairs(b._parts) do result[#result+1] = v end end
  return setmetatable({_parts=result}, Builder)
end
Builder.build = function(b) return table.concat(b._parts) end

local function S(s) return setmetatable({_parts={s}},Builder) end
print((S("Hello") .. ", " .. S("World") .. "!"):build())

비교 메타메서드

__eq는 테이블의 ==을 정의합니다(두 피연산자가 같은 메타테이블을 가질 때만 호출됩니다). __lt는 <를, __le는 <=를 정의합니다. 참고로 ~=는 __eq를 사용하고, >는 인자를 뒤집은 __lt를 사용하며, >=는 __le를 사용합니다.

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end

Vec.__eq = function(a,b) return a.x==b.x and a.y==b.y end
Vec.__lt = function(a,b)
  return (a.x^2+a.y^2) < (b.x^2+b.y^2)  -- by magnitude
end

print(V(1,2) == V(1,2))  -- true
print(V(1,2) == V(1,3))  -- false
print(V(1,0) < V(2,0))   -- true (1 < 2)

메타메서드 연결

메타메서드는 값을 반환하며 보통 새 테이블을 반환하므로 연산을 자연스럽게 연결할 수 있습니다. 각 연산자는 새 객체를 반환하고 다음 연산자는 그 결과를 사용합니다. 이를 통해 사용자 지정 형식에서도 유창한 산술 표현식을 사용할 수 있습니다.

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__add = function(a,b) return V(a.x+b.x,a.y+b.y) end
Vec.__mul = function(a,b)
  if type(b)=="number" then return V(a.x*b,a.y*b) end
  return V(a.x*b.x+a.y*b.y, 0)   -- simplified
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end

local result = V(1,0) + V(0,1) * 3
print(tostring(result))   -- (1,3)

혼합 형식 연산

vec * 2를 작성하면 왼쪽 피연산자는 테이블입니다. 2 * vec를 작성하면 왼쪽 피연산자가 숫자이므로(메타메서드가 없음) Lua는 오른쪽 피연산자의 메타테이블에서 __mul을 확인합니다. 메타메서드에서 두 피연산자 순서를 모두 처리하십시오.

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__tostring = function(v) return "("..v.x..","..v.y..")" end

Vec.__mul = function(a, b)
  if type(a) == "number" then
    return V(a*b.x, a*b.y)   -- scalar * vec
  elseif type(b) == "number" then
    return V(a.x*b, a.y*b)   -- vec * scalar
  end
  return a.x*b.x + a.y*b.y  -- dot product
end

print(tostring(V(1,2) * 3))  -- (3,6)
print(tostring(3 * V(1,2)))  -- (3,6)

빠른 확인

-myTable에 의해 호출되는 메타메서드는 무엇일까요?

복습: 산술 메타메서드

요약:

  • __add __sub __mul __div __mod __pow __idiv — 이항 산술
  • __unm — 단항 마이너스, __len — # 연산자
  • __concat — .. 연산자
  • __eq __lt __le — 비교 연산자
  • 메타메서드 내부에서 혼합 형식(숫자/테이블)을 처리하십시오

자주 묻는 질문

“산술 메타메서드” 강의는 무료인가요?

네 — “산술 메타메서드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Lua Academy 강의 전체를 잠금 해제할 수 있습니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“산술 메타메서드”에서 뭘 배우나요?

메타메서드로 +,-,*,/ 및 기타 연산자의 동작을 재정의합니다. 브라우저에서 직접 실행하는 실습 코드로 Lua Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Lua Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Lua Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“산술 메타메서드” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Lua Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Lua Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 메타테이블 입문
  2. __index와 __newindex
  3. 산술 메타메서드
  4. __tostring과 __len
← Lua Academy(으)로 돌아가기