Sök…
Syntax
- funcname = funktion (paramA, paramB, ...) kropp; return exprlist end - en enkel funktion
- funktion funcname (paramA, paramB, ...) kropp; return exprlist end - korthet för ovan
- lokal funcname = funktion (paramA, paramB, ...) kropp; return exprlist end - en lambda
- lokalt funknamn ; funcname = funktion (paramA, paramB, ...) kropp; return exprlist end - lambda som kan göra rekursiva samtal
- lokal funktion funcname (paramA, paramB, ...) kropp; return exprlist end - korthet för ovan
- funcname (paramA, paramB, ...) - ringa en funktion
- local var = var eller "Standard" - en standardparameter
- return nil, "felmeddelanden" - standard sätt att avbryta med ett fel
Anmärkningar
Funktioner är vanligtvis inställda med function a(b,c) ... end
och sällan med inställning av en variabel till en anonym funktion ( a = function(a,b) ... end
). Det motsatta är sant när man skickar funktioner som parametrar, anonyma funktioner används oftast och normala funktioner används inte så ofta.
Definiera en funktion
function add(a, b)
return a + b
end
-- creates a function called add, which returns the sum of it's two arguments
Låt oss titta på syntaxen. Först ser vi ett function
. Det är ganska beskrivande. Nästa kommer vi att add
identifieraren; namnet. Vi ser då argumenten (a, b)
dessa kan vara vad som helst och de är lokala. Endast inuti funktionskroppen kan vi komma åt dem. Låt oss hoppa till slutet, vi ser ... ja, end
! Och allt däremellan är funktionskroppen; koden som körs när den heter. Den return
sökord är det som gör funktionen faktiskt ge några användbara utgång. Utan den returnerar funktionen ingenting, vilket motsvarar noll. Detta kan naturligtvis vara användbart för saker som interagerar med IO, till exempel:
function printHello(name)
print("Hello, " .. name .. "!");
end
I den funktionen använde vi inte returrättet.
Funktioner kan också returnera värden på villkor, vilket innebär att en funktion har valet att returnera ingenting (noll) eller ett värde. Detta visas i följande exempel.
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
Det är också möjligt för en funktion att returnera flera värden separerade med komma, som visas:
function doOperations(a, b)
return a+b, a-b, a*b
end
added, subbed, multiplied = doOperations(4,2)
Funktioner kan också förklaras lokala
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
De kan också sparas i tabeller:
tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3
Ringer en funktion.
Funktioner är bara användbara om vi kan ringa dem. För att ringa en funktion används följande syntax:
print("Hello, World!")
Vi kallar print
. Med hjälp av argumentet "Hello, World"
. Som är uppenbart kommer detta att skriva ut Hello, World
till output stream. Det returnerade värdet är tillgängligt, precis som alla andra variabler skulle vara.
local added = add(10, 50) -- 60
Variabler accepteras också i en funktions parametrar.
local a = 10
local b = 60
local c = add(a, b)
print(c)
Funktioner som förväntar sig ett bord eller en sträng kan kallas med ett snyggt syntaktiskt socker: parenteser som omger samtalet kan utelämnas.
print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end
Anonyma funktioner
Skapa anonyma funktioner
Anonyma funktioner är precis som vanliga Lua-funktioner, förutom att de inte har ett namn.
doThrice(function()
print("Hello!")
end)
Som ni ser är funktionen inte tilldelad något namn som print
eller add
. För att skapa en anonym funktion behöver du bara utelämna namnet. Dessa funktioner kan också ta argument.
Förstå det syntaktiska sockret
Det är viktigt att förstå att följande kod
function double(x)
return x * 2
end
är faktiskt bara en kortfattning för
double = function(x)
return x * 2
end
Men funktionen ovan är inte anonym eftersom funktionen direkt tilldelas en variabel!
Funktioner är förstklassiga värden
Detta innebär att en funktion är ett värde med samma rättigheter som konventionella värden som nummer och strängar. Funktioner kan lagras i variabler, i tabeller, kan skickas som argument och kan returneras av andra funktioner.
För att demonstrera detta skapar vi också en "halv" -funktion:
half = function(x)
return x / 2
end
Så nu har vi två variabler, half
och double
, som båda innehåller en funktion som ett värde. Tänk om vi ville skapa en funktion som skulle mata numret 4 i två givna funktioner och beräkna summan av båda resultaten?
Vi vill kalla den här funktionen som sumOfTwoFunctions(double, half, 4)
. Detta kommer att mata double
funktion, half
funktion och heltal 4
i vår egen funktion.
function sumOfTwoFunctions(firstFunction, secondFunction, input)
return firstFunction(input) + secondFunction(input)
end
Ovanstående sumOfTwoFunctions
funktion visar hur funktioner kan skickas runt i argument och nås med ett annat namn.
Standardparametrar
function sayHello(name)
print("Hello, " .. name .. "!")
end
Denna funktion är en enkel funktion och den fungerar bra. Men vad skulle hända om vi bara ringde 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 ?
Det är inte riktigt bra. Det finns två sätt att fixa detta:
Du återgår omedelbart från funktionen:
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
Du ställer in en standardparameter .
Använd detta enkla uttryck för att göra detta
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
Formspråket name = name or "Jack"
fungerar eftersom or
i Lua kortslutningar. Om objektet på vänster sida av ett or
är något annat än nil
eller false
, utvärderas aldrig den högra sidan. Å andra sidan, om sayHello
kallas utan parameter, kommer name
att vara nil
, och så kommer strängen "Jack"
att tilldelas name
. (Observera att detta formspråk därför inte fungerar om den booleska false
är ett rimligt värde för den aktuella parametern.)
Flera resultat
Funktioner i Lua kan ge flera resultat.
Till exempel:
function triple(x)
return x, x, x
end
För att spara dessa värden måste du använda följande syntax när du ringer till en funktion:
local a, b, c = triple(5)
Vilket kommer att resultera i a = b = c = 5
i detta fall. Det är också möjligt att ignorera returnerade värden med hjälp av kastningsvariabeln _
på önskad plats i en lista med variabler:
local a, _, c = triple(5)
I detta fall ignoreras det andra returnerade värdet. Det är också möjligt att ignorera returvärden genom att inte tilldela dem till någon variabel:
local a = triple(5)
Variabel a
tilldelas det första returvärdet och de återstående två kommer att kasseras.
När en variabel mängd resultat returneras av en funktion kan man lagra dem alla i en tabell genom att utföra funktionen inuti den:
local results = {triple(5)}
På detta sätt kan man iterera över results
att se vad funktionen returnerade.
Notera
Detta kan vara en överraskning i vissa fall, till exempel:
local t = {}
table.insert(t, string.gsub(" hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)
Detta händer eftersom string.gsub
returnerar 2 värden: den givna strängen, med förekomster av mönstret ersatt, och det totala antalet matchningar som inträffade.
För att lösa detta använder du antingen en mellanvariabel eller lägg ()
runt samtalet, så här:
table.insert(t, (string.gsub(" hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}
Detta tar bara det första resultatet av samtalet och ignorerar resten.
Variabelt antal argument
Namngivna argument
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
Kontrollera argumenttyper
Vissa funktioner fungerar bara på en viss typ av 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
detta har flera konsekvenser:
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 kan vi kalla funktionen med vilken parameter vi vill, och den kraschar inte programmet.
-- 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
Så, om vi har en funktion som gör något med en instans av en viss klass? Detta är svårt, eftersom klasser och objekt vanligtvis är tabeller, så type
kommer att returnera '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ösning: jämföra metatabellerna
-- 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
nedläggningar
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
typiskt användningsexempel
function new_adder(number)
return function(input)
return input + number
end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5
mer avancerat exempel
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