Suche…
Syntax
- funcname = function (paramA, paramB, ...) body; return exprlist end - eine einfache Funktion
- Funktion funcname (parama, paramB, ...) Körper; exprlist end zurückgeben - Abkürzung für oben
- local funcname = function (paramA, paramB, ...) body; return exprlist end - ein Lambda
- lokaler Funkname ; funcname = function (paramA, paramB, ...) body; return exprlist end - Lambda, das rekursive Aufrufe ausführen kann
- lokale Funktion Funkname (paramA, paramB, ...) body; exprlist end zurückgeben - Abkürzung für oben
- funcname (paramA, paramB, ...) - eine Funktion aufrufen
- local var = var oder "Default" - ein Standardparameter
- nil zurückgeben, "Fehlermeldungen" - Standardmethode zum Abbruch mit einem Fehler
Bemerkungen
Funktionen werden normalerweise mit der function a(b,c) ... end
und selten mit dem Setzen einer Variablen auf eine anonyme Funktion ( a = function(a,b) ... end
) gesetzt. Das Gegenteil ist der Fall, wenn Funktionen als Parameter übergeben werden, meistens anonyme Funktionen verwendet werden und normale Funktionen nicht so oft verwendet werden.
Funktion definieren
function add(a, b)
return a + b
end
-- creates a function called add, which returns the sum of it's two arguments
Schauen wir uns die Syntax an. Zuerst sehen wir ein function
. Nun, das ist ziemlich beschreibend. Als Nächstes sehen wir die Kennung zum add
. der Name. Wir sehen dann die Argumente (a, b)
diese können alles sein und sie sind lokal. Nur innerhalb des Funktionskörpers können wir auf sie zugreifen. Überspringen wir bis zum Ende, wir sehen ... nun, das end
! Und alles, was dazwischen ist, ist der Funktionskörper; Der Code, der ausgeführt wird, wenn er aufgerufen wird. Das Schlüsselwort return
bewirkt, dass die Funktion tatsächlich nützliche Ergebnisse liefert. Ohne diese Funktion gibt die Funktion nichts zurück, was gleichbedeutend mit der Rückgabe von Null ist. Dies kann natürlich für Dinge hilfreich sein, die mit IO interagieren, zum Beispiel:
function printHello(name)
print("Hello, " .. name .. "!");
end
In dieser Funktion haben wir die return-Anweisung nicht verwendet.
Funktionen können Werte auch bedingt zurückgeben, was bedeutet, dass eine Funktion die Wahl hat, nichts (null) oder einen Wert zurückzugeben. Dies wird im folgenden Beispiel gezeigt.
function add(a, b)
if (a + b <= 100) then
return a + b -- Returns a value
else
print("This function doesn't return values over 100!") -- Returns nil
end
end
Es ist auch möglich, dass eine Funktion mehrere durch Kommas getrennte Werte zurückgibt, wie gezeigt:
function doOperations(a, b)
return a+b, a-b, a*b
end
added, subbed, multiplied = doOperations(4,2)
Funktionen können auch als lokal deklariert werden
do
local function add(a, b) return a+b end
print(add(1,2)) --> prints 3
end
print(add(2, 2)) --> exits with error, because 'add' is not defined here
Sie können auch in Tabellen gespeichert werden:
tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3
Funktion aufrufen
Funktionen sind nur nützlich, wenn wir sie aufrufen können. Um eine Funktion aufzurufen, wird folgende Syntax verwendet:
print("Hello, World!")
Wir rufen die print
. Verwenden Sie das Argument "Hello, World"
. Es ist offensichtlich, dass Hello, World
in den Ausgabestrom gedruckt wird. Auf den zurückgegebenen Wert kann wie auf jede andere Variable zugegriffen werden.
local added = add(10, 50) -- 60
Variablen werden auch in den Parametern einer Funktion akzeptiert.
local a = 10
local b = 60
local c = add(a, b)
print(c)
Funktionen, die eine Tabelle oder einen String erwarten, können mit einem sauberen syntaktischen Zucker aufgerufen werden: Klammern, die den Aufruf umgeben, können weggelassen werden.
print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end
Anonyme Funktionen
Anonyme Funktionen erstellen
Anonyme Funktionen sind wie normale Lua-Funktionen, es sei denn, sie haben keinen Namen.
doThrice(function()
print("Hello!")
end)
Wie Sie sehen, ist die Funktion keinem Namen wie print
oder add
. Um eine anonyme Funktion zu erstellen, müssen Sie nur den Namen weglassen. Diese Funktionen können auch Argumente enthalten.
Den syntaktischen Zucker verstehen
Es ist wichtig zu verstehen, dass der folgende Code
function double(x)
return x * 2
end
ist eigentlich nur eine Abkürzung für
double = function(x)
return x * 2
end
Die obige Funktion ist jedoch nicht anonym, da die Funktion direkt einer Variablen zugeordnet ist!
Funktionen sind erstklassige Werte
Dies bedeutet, dass eine Funktion ein Wert ist, der dieselben Rechte wie herkömmliche Werte wie Zahlen und Strings hat. Funktionen können in Variablen gespeichert werden, in Tabellen, können als Argumente übergeben und von anderen Funktionen zurückgegeben werden.
Um dies zu demonstrieren, erstellen wir auch eine "halbe" Funktion:
half = function(x)
return x / 2
end
Jetzt haben wir zwei Variablen, die half
und die double
, die beide eine Funktion als Wert enthalten. Was wäre, wenn wir eine Funktion erstellen wollten, die die Zahl 4 in zwei gegebene Funktionen einspeist und die Summe der beiden Ergebnisse berechnet?
Wir wollen diese Funktion wie sumOfTwoFunctions(double, half, 4)
. Dies führt die double
, die half
und die Ganzzahl 4
in unsere eigene Funktion ein.
function sumOfTwoFunctions(firstFunction, secondFunction, input)
return firstFunction(input) + secondFunction(input)
end
Die obige sumOfTwoFunctions
Funktion zeigt, wie Funktionen innerhalb von Argumenten weitergegeben und mit einem anderen Namen aufgerufen werden können.
Standardparameter
function sayHello(name)
print("Hello, " .. name .. "!")
end
Diese Funktion ist eine einfache Funktion und funktioniert gut. Aber was würde passieren, wenn wir einfach sayHello()
?
stdin:2: attempt to concatenate local 'name' (a nil value)
stack traceback:
stdin:2: in function 'sayHello'
stdin:1: in main chunk
[C]: in ?
Das ist nicht gerade toll. Es gibt zwei Möglichkeiten, dies zu beheben:
Sie kehren sofort von der Funktion zurück:
function sayHello(name) if not (type(name) == "string") then return nil, "argument #1: expected string, got " .. type(name) end -- Bail out if there's no name. -- in lua it is a convention to return nil followed by an error message on error print("Hello, " .. name .. "!") -- Normal behavior if name exists. end
Sie legen einen Standardparameter fest .
Verwenden Sie dazu einfach diesen einfachen Ausdruck
function sayHello(name)
name = name or "Jack" -- Jack is the default,
-- but if the parameter name is given,
-- name will be used instead
print("Hello, " .. name .. "!")
end
Das Idiom name = name or "Jack"
funktioniert wegen or
bei Lua-Kurzschlüssen. Wenn das Element auf der linken Seite eines or
etwas anderes als nil
oder false
, wird die rechte Seite niemals ausgewertet. Wenn dagegen sayHello
ohne Parameter aufgerufen wird, ist name
gleich nil
und die Zeichenfolge "Jack"
wird dann dem name
zugewiesen. (Beachten Sie, dass dieses Idiom daher nicht funktioniert, wenn der boolesche Wert false
ein vernünftiger Wert für den betreffenden Parameter ist.)
Mehrere Ergebnisse
Funktionen in Lua können mehrere Ergebnisse zurückgeben.
Zum Beispiel:
function triple(x)
return x, x, x
end
Beim Aufrufen einer Funktion müssen Sie zum Speichern dieser Werte die folgende Syntax verwenden:
local a, b, c = triple(5)
Was in diesem Fall zu a = b = c = 5
. Es ist auch möglich, zurückgegebene Werte zu ignorieren, indem Sie die Einwegvariable _
an der gewünschten Stelle in einer Liste von Variablen verwenden:
local a, _, c = triple(5)
In diesem Fall wird der zweite zurückgegebene Wert ignoriert. Es ist auch möglich, Rückgabewerte zu ignorieren, indem sie keiner Variablen zugewiesen werden:
local a = triple(5)
Der Variablen a
wird der erste Rückgabewert zugewiesen und die verbleibenden zwei werden verworfen.
Wenn eine variable Anzahl von Ergebnissen von einer Funktion zurückgegeben wird, können Sie sie alle in einer Tabelle speichern, indem Sie die darin enthaltene Funktion ausführen:
local results = {triple(5)}
Auf diese Weise kann über die results
iteriert werden, um zu sehen, was die Funktion zurückgegeben hat.
Hinweis
Dies kann in einigen Fällen eine Überraschung sein, zum Beispiel:
local t = {}
table.insert(t, string.gsub(" hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)
Dies geschieht, weil string.gsub
2 Werte zurückgibt: die angegebene Zeichenfolge, wobei das Vorkommen des Musters ersetzt wurde, und die Gesamtzahl der Übereinstimmungen.
Um dies zu lösen, verwenden Sie entweder eine Zwischenvariable oder put ()
um den Aufruf herum, z.
table.insert(t, (string.gsub(" hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}
Dadurch wird nur das erste Ergebnis des Anrufs erfasst und der Rest ignoriert.
Variable Anzahl von Argumenten
Benannte Argumente
local function A(name, age, hobby)
print(name .. "is " .. age .. " years old and likes " .. hobby)
end
A("john", "eating", 23) --> prints 'john is eating years old and likes 23'
-- oops, seems we got the order of the arguments wrong...
-- this happens a lot, specially with long functions that take a lot of arguments
-- and where the order doesn't follow any particular logic
local function B(tab)
print(tab.name .. "is " .. tab.age .. " years old and likes " .. tab.hobby)
end
local john = {name="john", hobby="golf", age="over 9000", comment="plays too much golf"}
B(john)
--> will print 'John is over 9000 years old and likes golf'
-- I also added a 'comment' argument just to show that excess arguments are ignored by the function
B({name = "tim"}) -- can also be written as
B{name = "tim"} -- to avoid cluttering the code
--> both will print 'tim is nil years old and likes nil'
-- remember to check for missing arguments and deal with them
function C(tab)
if not tab.age then return nil, "age not defined" end
tab.hobby = tab.hobby or "nothing"
-- print stuff
end
-- note that if we later decide to do a 'person' class
-- we just need to make sure that this class has the three fields
-- age, hobby and name, and it will be compatible with these functions
-- example:
local john = ClassPerson.new("John", 20, "golf") -- some sort of constructor
john.address = "some place" -- modify the object
john:do_something("information") -- call some function of the object
C(john) -- this works because objects are *usually* implemented as tables
Argumenttypen prüfen
Einige Funktionen funktionieren nur für einen bestimmten Typ von Argumenten:
function foo(tab)
return tab.bar
end
--> returns nil if tab has no field bar, which is acceptable
--> returns 'attempt to index a number value' if tab is, for example, 3
--> which is unacceptable
function kungfoo(tab)
if type(tab) ~= "table" then
return nil, "take your useless " .. type(tab) .." somewhere else!"
end
return tab.bar
end
Dies hat mehrere Auswirkungen:
print(kungfoo(20)) --> prints 'nil, take your useless number somewhere else!'
if kungfoo(20) then print "good" else print "bad" end --> prints bad
foo = kungfoo(20) or "bar" --> sets foo to "bar"
Jetzt können wir die Funktion mit dem gewünschten Parameter aufrufen und das Programm nicht abstürzen.
-- if we actually WANT to abort execution on error, we can still do
result = assert(kungfoo({bar=20})) --> this will return 20
result = assert(kungfoo(20)) --> this will throw an error
Was ist, wenn wir eine Funktion haben, die etwas mit einer Instanz einer bestimmten Klasse tut? Dies ist schwierig, weil Klassen und Objekte in der Regel Tabellen sind, so dass die type
wird Funktion zurückgeben 'table'
.
local Class = {data="important"}
local meta = {__index=Class}
function Class.new()
return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua
object = Class.new()
fake = {}
print(type(object)), print(type(fake)) --> prints 'table' twice
Lösung: Vergleichen Sie die Metatabellen
-- continuation of previous code snippet
Class.is_instance(tab)
return getmetatable(tab) == meta
end
Class.is_instance(object) --> returns true
Class.is_instance(fake) --> returns false
Class.is_instance(Class) --> returns false
Class.is_instance("a string") --> returns false, doesn't crash the program
Class.is_instance(nil) --> also returns false, doesn't crash either
Verschlüsse
do
local tab = {1, 2, 3}
function closure()
for key, value in ipairs(tab) do
print(key, "I can still see you")
end
end
closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
end
print(tab) --> nil
-- tab is out of scope
closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
-- the function can still see tab
typisches Anwendungsbeispiel
function new_adder(number)
return function(input)
return input + number
end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5
Fortgeschrittenes Anwendungsbeispiel
function base64.newDecoder(str) -- Decoder factory
if #str ~= 64 then return nil, "string must be 64 characters long!" end
local tab = {}
local counter = 0
for c in str:gmatch"." do
tab[string.byte(c)] = counter
counter = counter + 1
end
return function(str)
local result = ""
for abcd in str:gmatch"..?.?.?" do
local a, b, c, d = string.byte(abcd,1,-1)
a, b, c, d = tab[a], tab[b] or 0, tab[c] or 0, tab[d] or 0
result = result .. (
string.char( ((a<<2)+(b>>4))%256 ) ..
string.char( ((b<<4)+(c>>2))%256 ) ..
string.char( ((c<<6)+d)%256 )
)
end
return result
end
end