Разница между . и: в Lua

174

Я запутался в разнице между вызовами функций через .и через:

> x = {foo = function(a,b) return a end, bar = function(a,b) return b end, }
> return x.foo(3,4)
3
> return x.bar(3,4)
4
> return x:foo(3,4)
table: 0x10a120
> return x:bar(3,4)
3

Что :делает?

Джейсон С
источник
1
Связанный: stackoverflow.com/questions/3779671/…
finnw

Ответы:

237

Двоеточие предназначено для реализации методов, которые передаются selfв качестве первого параметра. Так x:bar(3,4)должно быть так же, как x.bar(x,3,4).

BMitch
источник
55
ах ... так что это объектно-ориентированный синтаксический сахар.
Джейсон С
7
Именно. Во всем справочном руководстве единственное упоминание, которое они приводят, это «Синтаксис двоеточия используется для определения методов, то есть функций, которые имеют неявный дополнительный параметр self». (Руководство 5.0, нижняя часть pdf страницы 19)
BMitch
2
ооооо ... я собирался спросить, где официальные документы по этому вопросу, но вы меня опередили. красиво сделано :-)
Jason S
1
@keyle Это зависит от того, selfбудет ли объект указываться в качестве первого параметра, и от значения его свойств.
Hydroper
8
Синтаксис @keyle Colon будет немного быстрее, если вызываемый вами объект не будет локальным, поскольку виртуальная машина получает его только один раз. В основном точка синтаксиса вроде object.method(object,args)извлекается objectдважды, а object:method(arg)извлекается objectтолько один раз. Если objectэто глобальное, повышенное значение или поле таблицы, то :оно быстрее, чем .. .никогда не быстрее чем :.
Негамартин
28

Для определения это в точности то же самое, что и указание self вручную - при компиляции он даже выдаст тот же байт-код. Т.е. function object:method(arg1, arg2)такой же как и function object.method(object, arg1, arg2).

При использовании :это почти то же самое, что .и специальный вид вызова, который будет использоваться внутри, чтобы убедиться, что objectлюбые возможные побочные эффекты вычислений / доступа рассчитываются только один раз. Вызов object:method(arg1, arg2)в остальном такой же как object.method(object, arg1, arg2).

Олег В. Волков
источник
21

Чтобы быть полностью точным, так obj:method(1, 2, 3)же, как

do
  local _obj = obj
  _obj.method(_obj, 1, 2, 3)
end

Почему локальная переменная? Потому что, как отмечали многие, obj:method()только индексы _ENVможно получить один раз obj. Это обычно просто важно при рассмотрении скорости, но рассмотрим следующую ситуацию:

local tab do
  local obj_local = { method = function(self, n) print n end }
  tab = setmetatable({}, {__index = function(idx)
    print "Accessing "..idx
    if idx=="obj" then return obj_local end
  end})
end
tab.obj.method(tab.obj, 20)
--> Accessing obj
--> Accessing obj
--> 20
tab.obj:method(10)
--> Accessing obj
--> 10

Теперь представьте, что __indexметаметод сделал больше, чем просто напечатал что-то. Представьте, что он увеличил счетчик, записал что-то в файл или удалил случайного пользователя из вашей базы данных. Есть большая разница между этим дважды или только один раз. В этом случае есть четкая разница между obj.method(obj, etc)и obj:method(etc).

DarkWiiPlayer
источник
Вы действительно не должны беспокоиться о таких вещах. Если вам нужно, то с вашей архитектурой что-то ужасно не так.
говорит
2
Я бы сказал, что все наоборот; Хороший код не должен делать никаких предположений о деталях реализации несвязанного кода. Вызовы функций могут запоминаться или не запоминаться, но это не означает, что рекомендуется вызывать их чаще, чем нужно.
DarkWiiPlayer