Różnica pomiędzy . i: w Lua

174

Jestem zdezorientowany różnicą między wywołaniami funkcji przez .i przez:

> 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

Co to :robi?

Jason S.
źródło

Odpowiedzi:

237

Dwukropek służy do implementowania metod, które są przekazywane selfjako pierwszy parametr. Więc x:bar(3,4)powinno być takie samo jak x.bar(x,3,4).

BMitch
źródło
55
ach ... więc jest to cukier składniowy zorientowany obiektowo.
Jason S,
7
Dokładnie. W całym podręczniku, jedyna notatka na ten temat to „Składnia dwukropka jest używana do definiowania metod, to znaczy funkcji, które mają ukryty dodatkowy parametr self”. (Instrukcja 5.0, dół strony 19)
BMitch
2
ooh ahh ... Chciałem zapytać, gdzie są w tej sprawie oficjalni doktorzy, ale mnie pokonałeś. ładnie wykonane. :-)
Jason S
1
@keyle Zależy to od selfobiektu, który przejdzie jako pierwszy parametr i jego wartość właściwości.
Hydroper
8
Składnia @keyle Colon byłaby trochę szybsza, gdyby wywoływany obiekt nie jest lokalny, ponieważ maszyna wirtualna pobiera go tylko raz. Zasadniczo składnia kropkowa, taka jak object.method(object,args)pobieranie objectdwa razy, podczas gdy object:method(arg)pobiera objecttylko raz. Jeśli objectjest polem globalnym, upvalue lub tabeli, to :jest szybsze niż .. .nigdy nie jest szybszy niż :.
negamartin
28

Z definicji jest to dokładnie to samo, co ręczne określanie siebie - przy kompilacji wygeneruje nawet ten sam kod bajtowy. To function object:method(arg1, arg2)jest to samo co function object.method(object, arg1, arg2).

Używanie :jest prawie takie samo, jak .- specjalny rodzaj połączenia będzie używany wewnętrznie, aby się upewnićobject wszelkie możliwe skutki uboczne obliczeń / dostępu są obliczane tylko raz. W object:method(arg1, arg2)innym przypadku dzwonienie jest takie samo jak object.method(object, arg1, arg2).

Oleg V. Volkov
źródło
21

Aby być całkowicie precyzyjnym, obj:method(1, 2, 3)oznacza to samo, co

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

Dlaczego zmienna lokalna? Ponieważ, jak wielu zauważyło, obj:method()indeksy _ENVmożna uzyskać tylko raz obj. Zwykle jest to ważne przy rozważaniu szybkości, ale rozważ taką sytuację:

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

A teraz wyobraź sobie, że __indexmetametoda zrobiła coś więcej niż tylko wydrukowanie czegoś. Wyobraź sobie, że zwiększył licznik, zalogował coś do pliku lub usunął przypadkowego użytkownika z bazy danych. Istnieje duża różnica między zrobieniem tego dwa razy a tylko raz. W tym przypadku istnieje wyraźna różnica między obj.method(obj, etc)i obj:method(etc).

DarkWiiPlayer
źródło
Naprawdę nie powinieneś się martwić takimi rzeczami. Jeśli musisz, jest coś strasznie nie tak z twoją architekturą.
Val mówi Przywróć Monikę
2
Powiedziałbym, że jest odwrotnie; dobry kod nie powinien przyjmować żadnych założeń dotyczących szczegółów implementacji niepowiązanego kodu. Wywołania funkcji mogą być zapamiętywane lub nie, co nie oznacza, że ​​dobrym zwyczajem jest dzwonienie do nich częściej niż to konieczne.
DarkWiiPlayer