Diferencia entre . y: en Lua

174

Estoy confundido acerca de la diferencia entre llamadas de función vía .y vía:

> 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

Que esta :haciendo

Jason S
fuente

Respuestas:

237

Los dos puntos son para implementar métodos que pasan selfcomo primer parámetro. Entonces x:bar(3,4)debería ser lo mismo que x.bar(x,3,4).

BMitch
fuente
55
ah ... entonces es azúcar sintáctico orientado a objetos.
Jason S
77
Exactamente. En todo el manual de referencia, la única propaganda que dan sobre esto es "La sintaxis de dos puntos se usa para definir métodos, es decir, funciones que tienen un parámetro adicional implícito". (5.0 manual, parte inferior de la página 19 en pdf)
BMitch
2
ooh ahh ... iba a preguntar dónde estaban los documentos oficiales sobre esto, pero me ganaste. bien hecho. :-)
Jason S
1
@keyle Depende del selfobjeto que irá como primer parámetro y su valor de propiedades.
Hydroper
8
La sintaxis de @keyle Colon sería un poco más rápida si el objeto al que llama no es local, ya que la máquina virtual lo recupera solo una vez. Básicamente, la sintaxis de puntos como object.method(object,args)recupera objectdos veces, mientras que object:method(arg)recupera objectsolo una vez. Si objectes un campo global, de valor superior o de tabla, entonces :es más rápido que .. .nunca es más rápido que :.
negamartin
28

Por definición, es exactamente lo mismo que especificarse manualmente, incluso producirá el mismo código de bytes en la compilación. Es decir function object:method(arg1, arg2)es lo mismo que function object.method(object, arg1, arg2).

El uso :es casi lo mismo que .: se utilizará un tipo especial de llamada internamente para asegurarse de que objectlos posibles efectos secundarios de los cálculos / acceso se calculen solo una vez. Llamar object:method(arg1, arg2)es lo mismo que object.method(object, arg1, arg2).

Oleg V. Volkov
fuente
21

Para ser completamente preciso, obj:method(1, 2, 3)es lo mismo que

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

¿Por qué la variable local? Porque, como muchos han señalado, obj:method()solo indexa _ENVuna vez para obtener obj. Esto normalmente solo es importante cuando se considera la velocidad, pero considere esta situación:

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

Ahora imagine que el __indexmetametodo hizo más que simplemente imprimir algo. Imagine que aumenta un contador, registra algo en un archivo o elimina un usuario aleatorio de su base de datos. Hay una gran diferencia entre hacerlo dos veces o solo una vez. En este caso, hay una clara diferencia entre obj.method(obj, etc)y obj:method(etc).

DarkWiiPlayer
fuente
Realmente no deberías preocuparte por esas cosas. Si tiene que hacerlo, hay algo terriblemente mal con su arquitectura.
Val dice Reinstate Monica
2
Yo diría que es al revés; un buen código no debe hacer suposiciones sobre los detalles de implementación de código no relacionado. Las llamadas a funciones pueden o no ser memorizadas, eso no significa que sea una buena práctica llamarlas con más frecuencia de la necesaria.
DarkWiiPlayer