Допустим, у вас есть "2d vector" "класс", который имеет .x
и .y
. Вы пишете dotproduct
функцию для них:
function dotproduct(a, b)
return a.x * b.x + a.y * b.y
end
Самый простой способ написать foo:dot(bar)
- это установить свойство .dot
для каждого нового экземпляра:
-- Make a "V2" namespace
local V2 = {}
function V2.new(x, y)
return {
x = x,
y = y,
-- Attach any methods to the object, also
dot = dotproduct,
}
end
Однако с ростом числа методов ситуация ухудшается по двум причинам. Во-первых, ваши объекты становятся больше - даже если вам нужно держать только два числа в каждом векторе, большинство хеш-таблиц - это функции, которые всегда одинаковы! Другая причина заключается в том, что множественные конструкторы становятся сложными, потому что вам нужно дублировать работу в каждом конструкторе.
Решением этой проблемы является __index
метатабельный. Вместо того, чтобы копировать каждый метод на каждый новый объект, вы можете просто указать, где найти «недостающие» методы по мере необходимости:
V2.dot = dotproduct
function V2.new(x, y)
return setmetatable({x = x, y = y}, {__index = V2})
end
У этой конструкции есть небольшой недостаток. Во-первых, вы выделяете новую метатаблицу в дополнение к каждой таблице, что является пустой тратой памяти (все метатаблицы одинаковы). Во-вторых, у вас нет возможности переопределить другие методы, не увеличивая эту новую метатабельность (то же раздражение, что и раньше).
Итак, мы просто делаем V2
метатабельным * :
V2.__index = V2
function V2.new(x, y)
return setmetatable({x = x, y = y}, V2)
end