Fehlerbehandlung
Lua kennt keine Ausnahmeklassen: error löst einen Fehler aus, pcall (protected call) fängt ihn ab.
local function teile(a, b)
if b == 0 then error("Division durch null") end
return a / b
end
print(pcall(teile, 10, 2))
local ok, meldung = pcall(teile, 1, 0)
print(ok, meldung)
local ok2, fehler = pcall(function() local x = nil; return x.feld end)
print(ok2, fehler)
local ok3, tabelle = pcall(error, {code = 404, text = "nicht gefunden"}) -- beliebige Werte als Fehler
print(ok3, tabelle.code, tabelle.text)
print(select(2, pcall(error, "ohne Position", 0))) -- Level 0: ohne Quelltextposition
print(pcall(assert, false, "Assertion fehlgeschlagen"))
print(pcall(assert, 1 == 1, "nie sichtbar"))
local function mit_traceback()
return xpcall(function() error("tief unten") end, function(m) return "behandelt: " .. m end)
end
print(mit_traceback())
local f, err = io.open("/gibt/es/nicht.txt", "r") -- viele Funktionen liefern nil, Fehlertext
print(f, err)
print(tonumber("x"), tostring(nil))Ausgabe
true 5.0 false main.lua:2: Division durch null false main.lua:10: attempt to index a nil value (local 'x') false 404 nicht gefunden ohne Position false Assertion fehlgeschlagen true true nie sichtbar false behandelt: main.lua:21: tief unten nil /gibt/es/nicht.txt: No such file or directory nil nil
Konvention: Funktionen, die scheitern können, liefern entweder nil, "Fehlertext" oder lösen error aus.
Dateien
local pfad = os.tmpname()
local f = assert(io.open(pfad, "w"))
f:write("Zeile 1\n", "Zeile 2\n")
f:write(string.format("%s hat %d Punkte\n", "Mia", 120))
f:close()
for zeile in io.lines(pfad) do print(#zeile, zeile) end
local f2 = assert(io.open(pfad, "a"))
f2:write("Zeile 4\n")
f2:close()
local f3 = assert(io.open(pfad))
local inhalt = f3:read("a") -- "a": alles
f3:close()
print(#inhalt, select(2, inhalt:gsub("\n", "")))
local f4 = assert(io.open(pfad))
print(f4:read("l")) -- eine Zeile
print(f4:read("n")) -- eine Zahl (nil, wenn keine)
f4:close()
os.remove(pfad)
print((io.open(pfad)))Ausgabe
7 Zeile 1 7 Zeile 2 18 Mia hat 120 Punkte 43 4 Zeile 1 nil nil
Module
Ein Modul ist eine Datei, die eine Tabelle zurückgibt. require lädt sie genau einmal:
-- Datei mathe_helfer.lua
local M = {}
function M.quadrat(x) return x * x end
function M.summe(t) local s = 0 for _, v in ipairs(t) do s = s + v end return s end
M.VERSION = "1.0"
return M
-- In einer anderen Datei
-- local helfer = require("mathe_helfer")
-- print(helfer.quadrat(5))Wir simulieren das, indem wir ein Modul in package.preload registrieren:
package.preload["mathe_helfer"] = function()
local M = {}
function M.quadrat(x) return x * x end
M.VERSION = "1.0"
return M
end
local helfer = require("mathe_helfer")
local nochmal = require("mathe_helfer")
print(helfer.quadrat(5), helfer.VERSION, helfer == nochmal)
print(package.loaded["mathe_helfer"] == helfer)
print(pcall(require, "gibt_es_nicht") == false)Ausgabe
25 1.0 true true true
Coroutinen
Eine Coroutine ist eine Funktion, die sich anhalten (yield) und später fortsetzen (resume) lässt. Das ermöglicht Generatoren, Zustandsmaschinen und kooperatives Multitasking:
local co = coroutine.create(function(a, b)
print("Start mit", a, b)
local c = coroutine.yield(a + b)
print("fortgesetzt mit", c)
local d, e = coroutine.yield(c * 2)
print("nochmal", d, e)
return "fertig"
end)
print(coroutine.resume(co, 1, 2))
print(coroutine.status(co))
print(coroutine.resume(co, 10))
print(coroutine.resume(co, "x", "y"))
print(coroutine.status(co))
print(coroutine.resume(co))
-- Generator mit wrap
local function zaehle(bis)
return coroutine.wrap(function()
for i = 1, bis do coroutine.yield(i) end
end)
end
for n in zaehle(4) do io.write(n, " ") end
print()
local function fib()
return coroutine.wrap(function()
local a, b = 0, 1
while true do coroutine.yield(a); a, b = b, a + b end
end)
end
local g, liste = fib(), {}
for _ = 1, 10 do liste[#liste + 1] = g() end
print(table.concat(liste, " "))Ausgabe
Start mit 1 2 true 3 suspended fortgesetzt mit 10 true 20 nochmal x y true fertig dead false cannot resume dead coroutine 1 2 3 4 0 1 1 2 3 5 8 13 21 34
Kooperatives Multitasking
local aufgaben = {}
local function starte(name, schritte)
aufgaben[#aufgaben + 1] = {name = name, co = coroutine.create(function()
for i = 1, schritte do
print(name .. " Schritt " .. i)
coroutine.yield()
end
end)}
end
starte("A", 3); starte("B", 2)
while #aufgaben > 0 do
for i = #aufgaben, 1, -1 do
local t = aufgaben[i]
coroutine.resume(t.co)
if coroutine.status(t.co) == "dead" then table.remove(aufgaben, i) end
end
end
print("alle fertig")Ausgabe
B Schritt 1 A Schritt 1 B Schritt 2 A Schritt 2 A Schritt 3 alle fertig
Merke
error,pcall,xpcall,assertfür Fehler; Rückgabenil, meldungals Konvention- Dateien:
io.open,f:read,f:write,io.lines, immerclose - Module: Datei gibt Tabelle zurück,
requirelädt einmal - Coroutinen:
create/wrap,resume,yield,status
Aufgabe
Schreibe einen Generator, der die Primzahlen bis 50 liefert, mit coroutine.wrap.