Diferença entre . e: em Lua

174

Estou confuso sobre a diferença entre chamadas de função via .e via:

> 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

O que está :fazendo?

Jason S
fonte

Respostas:

237

Os dois pontos são para implementar métodos que passam selfcomo o primeiro parâmetro. Então x:bar(3,4)deve ser o mesmo que x.bar(x,3,4).

BMitch
fonte
55
ah ... então é açúcar sintático orientado a objetos.
Jason S
7
Exatamente. Em todo o manual de referência, o único resumo que eles dão sobre isso é "A sintaxe do cólon é usada para definir métodos, ou seja, funções que possuem um parâmetro extra implícito". (5.0 manual, parte inferior da página em pdf 19)
BMitch 6/11/11
2
ooh ahh ... Eu ia perguntar onde estavam os documentos oficiais, mas você me venceu. bem feito. :-)
Jason S
1
@keyle Depende do selfobjeto que será o primeiro parâmetro e seu valor de propriedades.
Hydroper
8
A sintaxe @keyle Colon seria um pouco mais rápida se o objeto que você está chamando não for local, pois a máquina virtual o recupera apenas uma vez. Basicamente, sintaxe de ponto, como object.method(object,args)recupera objectduas vezes, enquanto object:method(arg)recupera objectapenas uma vez. Se objectfor um campo global, de valor superior ou de tabela, :será mais rápido que .. .nunca é mais rápido que :.
Negamartin
28

Para definição, é exatamente o mesmo que especificar manualmente - ele até produzirá o mesmo bytecode na compilação. Ou seja, function object:method(arg1, arg2)é o mesmo que function object.method(object, arg1, arg2).

O uso :é quase o mesmo que .- um tipo especial de chamada será usado internamente para garantir que objecttodos os possíveis efeitos colaterais dos cálculos / acesso sejam calculados apenas uma vez. Chamar object:method(arg1, arg2)é o mesmo que object.method(object, arg1, arg2).

Oleg V. Volkov
fonte
21

Para ser completamente preciso, obj:method(1, 2, 3)é o mesmo que

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

Por que a variável local? Porque, como muitos apontaram, obj:method()apenas os índices são obtidos _ENVuma vez obj. Isso normalmente é importante ao considerar a velocidade, mas considere esta situação:

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

Agora imagine que o __indexmetamétodo fez mais do que apenas imprimir algo. Imagine que ele aumentou um contador, registrou algo em um arquivo ou excluiu um usuário aleatório do seu banco de dados. Há uma grande diferença entre fazer isso duas ou apenas uma vez. Nesse caso, há uma clara diferença entre obj.method(obj, etc)e obj:method(etc).

DarkWiiPlayer
fonte
Você realmente não deveria se preocupar com essas coisas. Se você precisar, há algo terrivelmente errado com sua arquitetura.
val diz Reintegrar Monica
2
Eu diria que é o contrário; código bom não deve fazer suposições sobre detalhes de implementação de código não relacionado. As chamadas de função podem ou não ser memorizadas, o que não significa que é uma boa prática chamá-las com mais frequência do que o necessário.
DarkWiiPlayer 16/01/19