Metamethoden können verwendet werden, um zu definieren, wie Hollywoods Operatoren sich mit Tabellen verhalten soll. Normalerweise können Sie Hollywoods Operatoren nicht mit Tabellen verwenden. Beispielsweise ist die folgendes nicht möglich:
table_A = {1, 2, 3, 4, 5} table_B = {5, 4, 3, 2, 1} result = table_A + table_B ; erzeugt Compilerfehler! |
Der obige Code versucht table_A
mit table_B
zu addieren, aber das funktioniert
nicht, weil Tabellen Zufallsdaten enthalten können (Funktionen, Untertabellen,
Zeichenketten, etc.). So gibt es keine Möglichkeit zu sagen, wie der Add-Operator
sich bei einer Tabelle verhalten soll. Dies ist der Moment, wo Metamethoden
ins Spiel kommen. Mit Metamethoden können Sie festlegen, wie ein Operator sich
zu verhalten hat, wenn er eine Tabelle als Operanden enthält. Mit anderen Worten,
mit Metamethoden können Sie eine Funktion definieren, die ausgeführt wird,
wenn ein Operator mit einer Tabelle verwendet wird. Diese Funktion berechnet
dann das Ergebnis.
Metamethoden sind keine globalen Einstellungen, sie sind für jede Tabelle privat. Wenn Sie eine Tabelle erstellen, wird sie keine Metamethoden haben. Versuchen Sie einen Operator auf dieser Tabelle zu verwenden, wird er fehlschlagen Für das Zuweisen einer Metamethode an einer Tabelle müssen Sie den Befehl SetMetaTable() verwenden. Ein Metatabelle ist eine Tabelle, die einen Satz von Metamethoden enthält. SetMetaTable() akzeptiert zwei Argumente: Das erste Argument ist der Name der Tabelle, der Sie eine Metamethode zuweisen wollen, und das zweite Argument ist die eigentliche Metatabelle, d.h. die Tabelle, die die Metamethoden enthält.
Lassen Sie uns nun ein Beispiel anschauen. Wir werden den Code von oben so mit Metamethoden neu schreiben, dass wir die beiden Tabellen addieren können.
mt = {} ; erstellt unsere Metatabelle Function mt.__add(a, b) Local sizeA = ListItems(a) ; Anzahl Elemente in Tabelle A Local sizeB = ListItems(b) ; Anzahl Elemente in Tabelle B Local result = {} ; erstellt resultierende Tabelle For Local k = 0 To Min(sizeA, sizeB) - 1 result[k] = a[k] + b[k] ; zufügen der addierten Elemente Next Return(result) ; resultierende Tabelle zurückgeben EndFunction table_A = {1, 2, 3, 4, 5} table_B = {5, 4, 3, 2, 1} SetMetaTable(table_A, mt) ; weist "mt" der Tabelle table_A... ; ...als Metatabelle zu result = table_A + table_B |
Die resultierende Tabelle wird fünf Elemente aufweisen, die alle auf 6 gesetzt
sind. Nun schauen wir, was der Code oben gemacht hat. Zuerst erstellen wir
eine leere Tabelle, die für uns als Metatabelle dient. Dann fügen wir eine
Funktion namens __add
(zwei Unterstriche verwenden) an diese Tabelle. Diese
Funktion wird die Metamethode für den + Operator. Beachten Sie, dass wir den
Namen __add
für diese Funktion verwenden müssen, weil Hollywood anhand des
Funktionsnamen den Operator erkennt, die die Metamethode verkörpert. Mit __add
als Name wird eine Metamethode für den Add-Operator (+) definiert. Der Code
in unserer Metamethode berechnet die Länge der beiden Tabellen, addiert zwei
Tabellenelemente und speichert sie in einer Ergebnistabelle, die sie zurückgibt.
Beachten Sie, dass die Umsetzung unserer __add
Metamethode oben erfordert,
dass beide Argumente Tabellen sind. Und die Tabellen dürfen nur Zahlen enthalten
(oder Zeichenketten, die in Zahlen umgewandelt werden können). Z.B. die folgenden
Ausdrücke würden mit der oben definierten Metamethode nicht funktionieren:
result = table_A + 10 ; --> Fehler, weil "10" ist keine Tabelle result = table_A + "Hello" ; --> gleicher Fehler |
Natürlich ist es möglich, Metamethoden zu schreiben, die diese Situationen umgehen kann. Sie müssen nur die Typen der Parameter überprüfen, die auf Ihre Metamethode übergeben werden. Dann können Sie abhängig vom Variablentyp benutzerdefinierte Aktionen ausführen.
Jetzt haben wir die Metamethode für den Add-Operator (+) kennengelernt. Natürlich können Sie eine Metamethode für jeden anderen Hollywood-Operator anwenden. Sie können auch Metamethoden für alle relationalen Operatoren erstellen (= <> <> <=> =), so dass Sie Tabellen direkt untereinander vergleichen können. Alles was Sie wissen müssen, ist der richtige Name für den Operator der Metamethode, so dass Sie ihn installieren können. Hier ist eine Liste aller verfügbaren Metamethoden und die entsprechenden Operatoren:
Wie Sie sehen können, gibt es keine Metamethoden für die >, >= und <> Operatoren. Dies liegt daran, weil Hollywood durch neu formulierte Bedingungen der bekannten Operatoren diese Resultate auf folgendem Weg liefern kann:
a <> b ist das gleiche wie Not (a = b) a > b ist das gleiche wie b < a a >= b ist das gleiche wie b <= a |
Wenn Sie zwei Tabellen miteinander vergleichen möchten, die beide mit Metamethoden
verknüpft sind, ohne die __eq
Metamethode aufrufen, müssen Sie den Befehl RawEqual()
verwenden. Dieser Befehl wird beide Tabellen nur als Referenz vergleichen,
ohne die Metamethoden aufzurufen.
Abweichende Metatabellen mit Binäroperatoren
Wie Sie sehen können, hat jede Tabelle seine eigene private Metatabelleeinstellung. Werden binäre Operatoren verwendet, kann es vorkommen, dass die beiden Operanden nicht die gleichen Metatabelle verwenden. Wie wählt jetzt Hollywood die Metatabelle für die Operanden aus? Dies hängt von verschiedenen Bedingungen ab:
Einschränkungen der relationalen Metamethoden
Sie haben oben bereits gelesen, dass die relationale Metamethoden nur dann funktionieren, wenn die beiden Operanden die gleichen Metatabelle verwenden. Jedoch gibt es eine weitere Einschränkung bei der Verwendung von relationalen Metamethoden: Sie werden nur dann aufgerufen, wenn die zwei Operanden Tabellen sind. Es ist nicht möglich, eine Tabelle mit einer Nummer oder einer Zeichenfolge mit einer Tabelle zu vergleichen etc. Die arithmetischen und bitweise Metamethoden können mit jedem Variablentyp arbeiten, aber die relationale Metamethoden sind auf das Vergleichen von Tabellen beschränkt.
Erweiterte Metamethoden
Bisher haben wir nur die relationalen, arithmetischen, bitweise und verkettete
Metamethoden angeschaut. Es gibt jedoch ein paar mehr Metamethoden, die Sie
verwenden können, nämlich __index
, __newindex
, __call
und __tostring
.
Hier ist eine detaillierte Beschreibung dieser Metamethoden:
__index:
mt = {} Function mt.__index(t, idx) Return(0) EndFunction t = {x = 10, y = 20} SetMetaTable(t, mt) NPrint(t.x, t.y, t.z) ; --> gibt 10 20 0 aus |
Ohne unsere Metatabelle würde der Aufruf von NPrint() scheitern,
weil z
wurde nicht initialisiert. Durch die Verwendung der Metatabelle
jedoch wird z
wieder automatisch auf 0 gesetzt, weil es nicht existiert.
Manchmal kann es notwendig werden, aus einer Tabelle ohne Metamethoden zu lesen. Sie können dies mit dem RawGet() Befehl tun. RawGet() wird nie eine Metamethode aufrufen. Wenn ein Index nicht vorhanden ist, wird Nil zurückgegeben.
__newindex:
mt = {} Function mt.__newindex(t, idx, val) NPrint("Blocked writing", val, "at index", idx) EndFunction t = {x = 10, y = 20} SetMetaTable(t, mt) t.z = 45 ; --> Blockiert Schreib 45 bei Index z |
Der obige Code setzt die Tabelle t
unter Schreibschutz. Sie können
keine Änderungen an der Tabelle vornehmen.
Manchmal kann es notwendig werden, in eine Tabelle ohne Metamethoden zu schreiben. Sie können dies mit dem RawGet() Befehl tun. RawGet() wird nie eine Metamethode aufrufen. Man könnte sogar mit dem RawSet() Befehl in schreibgeschützte Tabellen schreiben.
__call:
mt = {} Function mt.__call(t) Local c = ListItems(t) Local sum = 0 For Local k = 0 To c - 1 Do sum = sum + t[k] Return(sum / c) EndFunction t = {10, 23, 45, 5, 107, 45, 18, 46} SetMetaTable(t, mt) NPrint(t()) ; --> 37.375 |
Der obige Code gibt 37,375 zurück, welcher der Mittelwert der acht
gespeicherten Werte in der Tabelle t
ist.
__tostring:
__tostring
können Sie dieses Verhalten leicht ändern. Hier ist eine
Metamethode, die eine Zeichenkettendarstellung einer Tabelle erstellt:
mt = {} Function mt.__tostring(t) Local r$ For Local k=0 To ListItems(t)-1 Do r$=r$..t[k].." " Return(r$) EndFunction t = {"Jeff", "Andy", "Mike", "Dave"} SetMetaTable(t, mt) NPrint(t) ; --> Jeff Andy Mike Dave |
Der obige Code gibt "Jeff Andy Mike Dave" aus, weil unsere __tostring
Metamethode einfach alle Elemente der Tabelle verkettet hat.