2013-05-11 5 views
6

Sto avendo difficoltà a capire il motivo per cui v'è una differenza di comportamento del __index metamethod tra questi per esempio:Lua MetaTable Incoerenza

A = { __index = A } 
function A:speak() 
    print("I'm an A") 
end 
An_A = setmetatable({},A) 
An_A:speak() 

alzerà il seguente errore: lua: l.lua:8: attempt to call method 'speak' (a nil value)

Mentre

B = { __index = function(t,key) return B[key] end } 
function B:speak() 
    print("I'm an B") 
end 
An_B = setmetatable({},B) 
An_B:speak() 

Eseguirà come previsto, in uscita I'm an B.


Nel tentativo di capire perché questo era il caso che ho letto this sezione del PiL. Essa afferma che:

The use of the __index metamethod for inheritance is so common that Lua provides a shortcut. Despite the name, the __index metamethod does not need to be a function: It can be a table, instead. When it is a function, Lua calls it with the table and the absent key as its arguments. When it is a table, Lua redoes the access in that table.

La mia comprensione di ciò è che nel frammento che coinvolge 'A', __index = A causare l'accesso deve essere fatto nella tabella A (come da segmenet boldened della citazione di cui sopra). Se questo è il caso, non capisco perché la funzione associata al tasto "speak" non sia stata trovata. Nel tentativo di risolvere questo problema, ho deciso di implementare l'approccio della funzione nello snippet B, che restituisce il valore associato a key in B e ha funzionato. Sicuramente __index = A e (adattato da B) __index = function(t,key) return A[key] end hanno lo stesso effetto.

Qualsiasi chiarimento sarebbe molto apprezzato.

risposta

9

Quello che sta accadendo nel primo esempio è che A.__index == nil. Al momento della creazione 'A' sulla vostra prima linea qui:

A = { __index = A } 

Il lato destro della cessione 'A' viene valutata per nil in quanto non esiste ancora a questo punto. Di conseguenza, in seguito, quando si imposta il metatabella qui:

An_A = setmetatable({},A) 

finisce davvero per fare qualcosa di simile a questo:

An_A = setmetatable({}, {__index = nil}) 

Per farlo funzionare nel modo desiderato, è necessario assicurati che __index non sia nil. Ad esempio, assegnare dopo la costruzione della tabella:

A = {} 
A.__index = A 

function A:speak() 
    print("I'm an A") 
end 
An_A = setmetatable({},A) 
An_A:speak()    --> outputs I'm an A 
+0

Grazie per la grande spiegazione, non si è verificato a me questo sarebbe accaduto, avevo pensato che sarebbe come alcune altre lingue in cui qualcosa come 'f = lambda n: f (n) 'è valido. evviva :) – HennyH

+1

@HennyH: 'f = function (n) return f (n) end' sta bene in lua per lo stesso motivo. L'equivalente pitone in errore è 'd = {" __index ": d}', ma python avrà 'NameError' – Eric

+0

@Eric Suppongo che qualsiasi funzione ricorsiva segua lo stesso modello. – HennyH