Арифметические метаметоды
Перегружайте операторы +, -, *, / и другие операторы с помощью метаметодов.
«Арифметические метаметоды» — бесплатный урок Lua Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 для пользовательской длины
__len вызывается при вычислении #obj для таблицы без необработанной целочисленной последовательности. Определите его, чтобы возвращать осмысленную «длину» для таких типов, как множества, очереди или взвешенные контейнеры.
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 срабатывает для оператора .., если хотя бы у одного операнда есть этот метаметод. Это позволяет определить конкатенацию для пользовательских типов или создавать плавные предметно-ориентированные языки.
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) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.
Чему я научусь в уроке «Арифметические метаметоды»?
Перегружайте операторы +, -, *, / и другие операторы с помощью метаметодов. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Lua Academy?
Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Арифметические метаметоды»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Lua Academy?
Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Введение в метатаблицы
- __index и __newindex
- Арифметические метаметоды
- __tostring и __len