Zum Inhalt springen
Lua Anfรคnger 35 min

Tables, Metatables & Module

Tables als universelle Datenstruktur, Metatables fuer OOP und Operator-Overloading, sowie require und eigene Module schreiben.

Aktualisiert:
Inhaltsverzeichnis

Tables, Metatables & Module

Tables sind Luaโ€™s einzige komplexe Datenstruktur - aber eine extrem flexible. Mit Metatables baust du darauf OOP und alles andere.

Tables - eine Kurzauffrischung

-- Array
local arr = {10, 20, 30}

-- Dictionary
local dict = {name = "Anna", alter = 28}

-- Gemischt
local mix = {"Max", age = 34, "Anna"}
print(mix[1])    -- "Max"
print(mix[2])    -- "Anna" (der Name "age=34" zaehlt nicht zum Array)
print(mix.age)   -- 34

Table-Operationen

local t = {10, 20, 30}

t[4] = 40                   -- hinzufuegen per Index
table.insert(t, 50)          -- am Ende
table.insert(t, 1, 5)        -- am Anfang (Index 1)

table.remove(t)              -- letztes entfernen
table.remove(t, 1)           -- bei Index 1 entfernen

#t                           -- Laenge des Array-Teils
table.concat(t, ", ")        -- zu String verbinden: "5, 10, 20, 30, 40, 50"
table.sort(t)                -- in-place sortieren

table.sort mit Comparator

local personen = {
  {name = "Max", alter = 34},
  {name = "Anna", alter = 28},
  {name = "Leo", alter = 22}
}

table.sort(personen, function(a, b) return a.alter < b.alter end)

for _, p in ipairs(personen) do
  print(p.name, p.alter)
end
-- Leo  22
-- Anna 28
-- Max  34

Verschachtelt

local team = {
  leitung = { name = "Anna", rolle = "PM" },
  mitglieder = {
    { name = "Max", skills = {"Go", "Rust"} },
    { name = "Leo", skills = {"Python", "React"} }
  }
}

print(team.leitung.name)
print(team.mitglieder[1].skills[1])   -- "Go"

Referenzsemantik

Tables werden per Referenz uebergeben:

local a = {1, 2, 3}
local b = a           -- b zeigt auf dasselbe Table
b[1] = 99

print(a[1])   -- 99 (a wurde mit geaendert!)

Fuer eine Kopie brauchst du eine explizite Funktion:

local function kopie(t)
  local neu = {}
  for k, v in pairs(t) do
    neu[k] = v
  end
  return neu
end

(Das ist eine flache Kopie. Fuer tief-verschachtelte Strukturen brauchst du rekursive Logik oder eine Library.)

Metatables - die Magie

Eine Metatable ist ein Table, das einer anderen Table ein spezielles Verhalten gibt. Du legst Meta-Methoden ueber Keys wie __index, __add, __tostring fest.

Beispiel: __index fuer Default-Werte

local standard = { farbe = "rot", groesse = "mittel" }

local auto = setmetatable(
  { marke = "VW" },
  { __index = standard }
)

print(auto.marke)    -- "VW"
print(auto.farbe)    -- "rot" (aus standard!)

Wenn ein Key im Table nicht existiert, sucht Lua in der Metatableโ€™s __index.

Beispiel: __tostring

local punkt = setmetatable(
  {x = 3, y = 4},
  {
    __tostring = function(p)
      return "(" .. p.x .. ", " .. p.y .. ")"
    end
  }
)

print(punkt)    -- "(3, 4)"

Operator-Overloading mit __add

local function Vec2(x, y)
  return setmetatable(
    {x = x, y = y},
    {
      __add = function(a, b) return Vec2(a.x + b.x, a.y + b.y) end,
      __tostring = function(v) return "(" .. v.x .. ", " .. v.y .. ")" end
    }
  )
end

local a = Vec2(1, 2)
local b = Vec2(3, 4)
print(a + b)    -- "(4, 6)"

Klassen mit Metatables

Das Standardmuster fuer OOP:

local Person = {}
Person.__index = Person            -- wenn Instanz-Feld fehlt, in Person suchen

function Person.new(name, alter)
  local self = setmetatable({}, Person)
  self.name = name
  self.alter = alter
  return self
end

function Person:gruessen()
  print("Hallo, ich bin " .. self.name .. "!")
end

function Person:geburtstag()
  self.alter = self.alter + 1
end

local anna = Person.new("Anna", 28)
anna:gruessen()     -- "Hallo, ich bin Anna!"
anna:geburtstag()
print(anna.alter)   -- 29

Was passiert da?

  1. setmetatable({}, Person) - die neue Instanz schaut bei unbekannten Keys in Person
  2. Methoden mit function Person:name(...) bekommen automatisch self als ersten Parameter
  3. Der Aufruf anna:gruessen() wird zu gruessen(anna)

Vererbung

local Entwickler = setmetatable({}, {__index = Person})
Entwickler.__index = Entwickler

function Entwickler.new(name, alter, sprache)
  local self = Person.new(name, alter)
  setmetatable(self, Entwickler)
  self.sprache = sprache
  return self
end

function Entwickler:coden()
  print(self.name .. " schreibt " .. self.sprache)
end

local max = Entwickler.new("Max", 34, "Lua")
max:gruessen()     -- geerbt
max:coden()        -- eigene Methode

Module schreiben und laden

Ein eigenes Modul

Lege mathe.lua an:

local M = {}

function M.addieren(a, b)
  return a + b
end

function M.quadrat(n)
  return n * n
end

M.PI = 3.14159

return M

Das Modul ist einfach eine Table, die zurueckgegeben wird.

Einbinden mit require

-- main.lua
local mathe = require("mathe")   -- ohne .lua

print(mathe.addieren(3, 4))   -- 7
print(mathe.quadrat(5))       -- 25
print(mathe.PI)                -- 3.14159

Wichtig:

  • Der Modul-Name entspricht dem Dateinamen ohne .lua
  • require cached Module - mehrfaches require laedt die Datei nur einmal
  • Unterordner: require("utils.mathe") fuer utils/mathe.lua

package.path

Wo sucht require nach Modulen? In package.path:

print(package.path)

Du kannst erweitern, z.B. fuer ein lib/-Verzeichnis:

package.path = package.path .. ";./lib/?.lua"

Standard-Libraries

Lua hat eine kleine, aber nuetzliche Standard-Library:

  • string - String-Methoden (format, sub, gsub, โ€ฆ)
  • table - Table-Methoden (insert, remove, concat, sort)
  • math - Mathematik (pi, sqrt, random, โ€ฆ)
  • io - Ein-/Ausgabe (open, read, write)
  • os - Betriebssystem (time, date, exit)

Dateien lesen und schreiben

-- Lesen
local f = io.open("daten.txt", "r")
if f then
  local inhalt = f:read("*all")
  f:close()
  print(inhalt)
end

-- Schreiben
local f = io.open("ausgabe.txt", "w")
f:write("Hallo, Datei!\n")
f:close()

Praktisches Beispiel: Bibliothek

bibliothek.lua:

local M = {}

local Buch = {}
Buch.__index = Buch

function Buch.new(titel, autor)
  return setmetatable(
    {titel = titel, autor = autor, ausgeliehen = false},
    Buch
  )
end

function Buch:info()
  local status = self.ausgeliehen and "ausgeliehen" or "verfuegbar"
  return string.format("%s - %s (%s)", self.titel, self.autor, status)
end

function Buch:ausleihen()
  if self.ausgeliehen then return false end
  self.ausgeliehen = true
  return true
end

function Buch:zurueckgeben()
  self.ausgeliehen = false
end

M.Buch = Buch

return M

main.lua:

local bib = require("bibliothek")

local b1 = bib.Buch.new("1984", "Orwell")
local b2 = bib.Buch.new("Die Verwandlung", "Kafka")

print(b1:info())
b1:ausleihen()
print(b1:info())
b1:zurueckgeben()
print(b1:info())

Zusammenfassung

  • Tables sind Array + Dictionary + Objekt in einem
  • Metatables geben Tables spezielles Verhalten (__index, __add, __tostring, โ€ฆ)
  • Person.__index = Person + setmetatable ist das OOP-Standardmuster
  • Vererbung via verschachtelte Metatables
  • Eigene Module: Table anlegen, Funktionen reinhaengen, return am Ende
  • require laedt Module und cached sie
  • Standard-Library: string, table, math, io, os

Damit hast du Luaโ€™s Kern im Griff. Im weiteren Kurs vertiefen wir Coroutines, Metaprogramming und den Einsatz in Roblox/Neovim.

Zurรผck zum Lua Kurs