Zoeken…
Syntaxis
- funcname = function (paramA, paramB, ...) body; einde exprlist terug - een eenvoudige functie
- functienaam (paramA, paramB, ...) body; einde exprlist terug - steno voor hierboven
- lokale funcnaam = functie (paramA, paramB, ...) body; terugkeer exprlist einde - een lambda
- lokale functienaam ; funcname = function (paramA, paramB, ...) body; einde exprlist terug - lambda die recursieve oproepen kan doen
- lokale functienaam (paramA, paramB, ...) body; einde exprlist terug - steno voor hierboven
- funcname (paramA, paramB, ...) - roep een functie op
- lokale var = var of "Standaard" - een standaardparameter
- return nul, "foutmeldingen" - standaard manier om af te breken met een fout
Opmerkingen
Functies worden meestal ingesteld met function a(b,c) ... end
en zelden met het instellen van een variabele op een anonieme functie ( a = function(a,b) ... end
). Het tegenovergestelde is waar wanneer functies als parameters worden doorgegeven, anonieme functies meestal worden gebruikt en normale functies niet zo vaak worden gebruikt.
Een functie definiëren
function add(a, b)
return a + b
end
-- creates a function called add, which returns the sum of it's two arguments
Laten we de syntaxis bekijken. Eerst zien we een function
sleutelwoord. Nou, dat is behoorlijk beschrijvend. Vervolgens zien we de add
identifier; de naam. We zien dan de argumenten (a, b)
deze kunnen van alles zijn en ze zijn lokaal. Alleen binnen het functielichaam kunnen we er toegang toe krijgen. Laten we doorgaan tot het einde, we zien ... wel, het end
! En alles dat daar tussenin zit, is het functielichaam; de code die wordt uitgevoerd wanneer deze wordt aangeroepen. Het sleutelwoord return
zorgt ervoor dat de functie daadwerkelijk nuttige uitvoer oplevert. Zonder deze functie retourneert de functie niets, wat overeenkomt met het retourneren van nul. Dit kan natuurlijk handig zijn voor dingen die interageren met IO, bijvoorbeeld:
function printHello(name)
print("Hello, " .. name .. "!");
end
In die functie hebben we de retourverklaring niet gebruikt.
Functies kunnen ook waarden voorwaardelijk retourneren, wat betekent dat een functie de keuze heeft om niets (nul) of een waarde te retourneren. Dit wordt aangetoond in het volgende voorbeeld.
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
Het is ook mogelijk voor een functie om meerdere waarden te scheiden, gescheiden door komma's, zoals weergegeven:
function doOperations(a, b)
return a+b, a-b, a*b
end
added, subbed, multiplied = doOperations(4,2)
Functies kunnen ook lokaal worden verklaard
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
Ze kunnen ook in tabellen worden opgeslagen:
tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3
Functie aanroepen.
Functies zijn alleen nuttig als we ze kunnen aanroepen. Om een functie aan te roepen wordt de volgende syntaxis gebruikt:
print("Hello, World!")
We roepen de print
op. Met behulp van het argument "Hello, World"
. Zoals duidelijk is, wordt hiermee Hello, World
naar de outputstroom afgedrukt. De geretourneerde waarde is toegankelijk, net als elke andere variabele.
local added = add(10, 50) -- 60
Variabelen worden ook geaccepteerd in de parameters van een functie.
local a = 10
local b = 60
local c = add(a, b)
print(c)
Functies die een tafel of een string verwachten, kunnen worden opgeroepen met een nette syntactische suiker: haakjes rond de oproep kunnen worden weggelaten.
print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end
Anonieme functies
Anonieme functies maken
Anonieme functies zijn net als gewone Lua-functies, behalve dat ze geen naam hebben.
doThrice(function()
print("Hello!")
end)
Zoals u kunt zien, is de functie niet toegewezen aan een naam zoals print
of add
. Om een anonieme functie te maken, hoeft u alleen de naam weg te laten. Deze functies kunnen ook argumenten aannemen.
Inzicht in de syntactische suiker
Het is belangrijk om te begrijpen dat de volgende code
function double(x)
return x * 2
end
is eigenlijk maar een afkorting voor
double = function(x)
return x * 2
end
Bovenstaande functie is echter niet anoniem omdat de functie direct is toegewezen aan een variabele!
Functies zijn eerste klas waarden
Dit betekent dat een functie een waarde is met dezelfde rechten als conventionele waarden zoals getallen en strings. Functies kunnen worden opgeslagen in variabelen, in tabellen, kunnen worden doorgegeven als argumenten en kunnen worden geretourneerd door andere functies.
Om dit aan te tonen, maken we ook een "halve" functie:
half = function(x)
return x / 2
end
We hebben nu twee variabelen, half
en double
, die beide een functie als waarde bevatten. Wat als we een functie wilden maken die het getal 4 in twee gegeven functies zou invoeren en de som van beide resultaten zou berekenen?
We willen deze functie sumOfTwoFunctions(double, half, 4)
zoals sumOfTwoFunctions(double, half, 4)
. Hiermee worden de double
functie, de half
functie en het gehele getal 4
in onze eigen functie ingevoerd.
function sumOfTwoFunctions(firstFunction, secondFunction, input)
return firstFunction(input) + secondFunction(input)
end
De bovenstaande sumOfTwoFunctions
functie laat zien hoe functies binnen argumenten kunnen worden doorgegeven en met een andere naam kunnen worden gebruikt.
Standaard parameters
function sayHello(name)
print("Hello, " .. name .. "!")
end
Die functie is een eenvoudige functie en werkt goed. Maar wat zou er gebeuren als we gewoon sayHello()
zouden noemen?
stdin:2: attempt to concatenate local 'name' (a nil value)
stack traceback:
stdin:2: in function 'sayHello'
stdin:1: in main chunk
[C]: in ?
Dat is niet bepaald geweldig. Er zijn twee manieren om dit op te lossen:
U keert onmiddellijk terug van de functie:
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
U stelt een standaardparameter in .
Gebruik hiervoor deze eenvoudige uitdrukking
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
Het idioom name = name or "Jack"
werkt omdat or
in Lua kortsluiting. Als het item aan de linkerkant van een or
iets anders is dan nil
of false
, wordt de rechterkant nooit geëvalueerd. Aan de andere kant, als sayHello
zonder parameter wordt aangeroepen, is de name
nil
en wordt de tekenreeks "Jack"
toegewezen aan de name
. (Merk op dat dit idioom daarom niet zal werken als de Booleaanse false
een redelijke waarde is voor de parameter in kwestie.)
Meerdere resultaten
Functies in Lua kunnen meerdere resultaten opleveren.
Bijvoorbeeld:
function triple(x)
return x, x, x
end
Wanneer u een functie aanroept, moet u de volgende syntaxis gebruiken om deze waarden op te slaan:
local a, b, c = triple(5)
Wat in dit geval resulteert in a = b = c = 5
. Het is ook mogelijk om geretourneerde waarden te negeren door de wegwerpvariabele _
op de gewenste plaats in een lijst met variabelen te gebruiken:
local a, _, c = triple(5)
In dit geval wordt de tweede geretourneerde waarde genegeerd. Het is ook mogelijk om retourwaarden te negeren door ze niet aan een variabele toe te wijzen:
local a = triple(5)
Variabele a
krijgt de eerste retourwaarde toegewezen en de resterende twee worden genegeerd.
Wanneer een variabel aantal resultaten door een functie wordt geretourneerd, kan men ze allemaal in een tabel opslaan door de functie erin uit te voeren:
local results = {triple(5)}
Op deze manier kan men de results
doorlopen om te zien wat de functie heeft teruggegeven.
Notitie
Dit kan in sommige gevallen een verrassing zijn, bijvoorbeeld:
local t = {}
table.insert(t, string.gsub(" hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)
Dit gebeurt omdat string.gsub
2 waarden retourneert: de opgegeven tekenreeks, waarbij het patroon wordt vervangen en het totale aantal overeenkomsten dat heeft plaatsgevonden.
Om dit op te lossen, gebruikt u een tussenliggende variabele of put ()
rond de aanroep, als volgt:
table.insert(t, (string.gsub(" hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}
Dit pakt alleen het eerste resultaat van de oproep en negeert de rest.
Variabel aantal argumenten
Genoemde argumenten
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 controleren
Sommige functies werken alleen op een bepaald type argument:
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
dit heeft verschillende implicaties:
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"
nu kunnen we de functie aanroepen met welke parameter we maar willen, en het zal het programma niet laten crashen.
-- 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
Dus, wat als we een functie hebben die iets doet met een instantie van een specifieke klasse? Dit is moeilijk, omdat klassen en objecten meestal tabellen zijn, dus de type
retourneert '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
Oplossing: vergelijk de 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
sluitingen
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
typisch gebruiksvoorbeeld
function new_adder(number)
return function(input)
return input + number
end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5
meer geavanceerd gebruiksvoorbeeld
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