Szukaj…


Składnia

  • funcname = funkcja (paramA, paramB, ...) ciało; return exprlist end - prosta funkcja
  • funkcja funcname (paramA, paramB, ...) body; return exprlist end - skrót od powyższego
  • local funcname = funkcja (paramA, paramB, ...) body; return exprlist end - lambda
  • lokalna nazwa funcname ; funcname = funkcja (paramA, paramB, ...) ciało; return exprlist end - lambda, która może wykonywać połączenia rekurencyjne
  • funkcja lokalna funcname (paramA, paramB, ...) body; return exprlist end - skrót od powyższego
  • funcname (paramA, paramB, ...) - wywołuje funkcję
  • lokalny var = var lub „Default” - parametr domyślny
  • return zero, „komunikaty o błędach” - standardowy sposób przerwania z błędem

Uwagi

Funkcje są zwykle ustawiane przy pomocy function a(b,c) ... end a rzadko przy ustawianiu zmiennej na funkcję anonimową ( a = function(a,b) ... end ). Odwrotnie jest w przypadku przekazywania funkcji jako parametrów, najczęściej używane są funkcje anonimowe, a normalne funkcje nie są używane tak często.

Definiowanie funkcji

function add(a, b)
    return a + b
end
-- creates a function called add, which returns the sum of it's two arguments

Spójrzmy na składnię. Po pierwsze, widzimy słowo kluczowe function . Cóż, to dość opisowe. Następnie widzimy identyfikator add ; imię. Następnie widzimy argumenty (a, b) które mogą być dowolne i są lokalne. Możemy uzyskać do nich dostęp tylko w treści funkcji. Przejdźmy do końca, widzimy ... no cóż, do end ! A wszystko, co pomiędzy, to ciało funkcyjne; kod uruchamiany po wywołaniu. return słowo kluczowe sprawia, że funkcja faktycznie daje użyteczne wyniki. Bez tego funkcja nic nie zwraca, co jest równoważne zwróceniu wartości zero. Może to oczywiście być przydatne w przypadku rzeczy wchodzących w interakcje z IO, na przykład:

function printHello(name)
    print("Hello, " .. name .. "!");
end 

W tej funkcji nie korzystaliśmy z instrukcji return.

Funkcje mogą również zwracać wartości warunkowo, co oznacza, że funkcja ma wybór, aby nic nie zwracać (zero) lub wartość. Pokazano to w poniższym przykładzie.

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

Możliwe jest również, że funkcja zwraca wiele wartości oddzielonych przecinkami, jak pokazano:

function doOperations(a, b)
    return a+b, a-b, a*b
end

added, subbed, multiplied = doOperations(4,2)

Funkcje można również zadeklarować jako lokalne

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

Można je również zapisać w tabelach:

tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3

Wywołanie funkcji.

Funkcje są przydatne tylko wtedy, gdy możemy je wywołać. Aby wywołać funkcję, używana jest następująca składnia:

print("Hello, World!")

Wzywamy funkcję print . Za pomocą argumentu "Hello, World" . Jak oczywiste, to wypisze Hello, World do strumienia wyjściowego. Zwrócona wartość jest dostępna, tak jak każda inna zmienna.

local added = add(10, 50) -- 60

Zmienne są również akceptowane w parametrach funkcji.

local a = 10
local b = 60

local c = add(a, b)

print(c)

Funkcje oczekujące tabeli lub łańcucha można wywoływać przy użyciu czystego cukru syntaktycznego: nawiasy otaczające wywołanie można pominąć.

print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end

Funkcje anonimowe

Tworzenie anonimowych funkcji

Anonimowe funkcje są jak zwykłe funkcje Lua, tyle że nie mają nazwy.

doThrice(function()
    print("Hello!")
end)

Jak widać, funkcja nie jest przypisana do żadnej nazwy, np. print lub add . Aby utworzyć anonimową funkcję, wystarczy pominąć nazwę. Funkcje te mogą również przyjmować argumenty.

Zrozumienie cukru syntaktycznego

Ważne jest, aby zrozumieć, że poniższy kod

function double(x)
    return x * 2
end

jest właściwie tylko skrótem

double = function(x)
    return x * 2
end

Jednak powyższa funkcja nie jest anonimowa, ponieważ funkcja jest bezpośrednio przypisana do zmiennej!

Funkcje są wartościami pierwszej klasy

Oznacza to, że funkcja jest wartością o takich samych prawach jak wartości konwencjonalne, takie jak liczby i ciągi znaków. Funkcje mogą być przechowywane w zmiennych, w tabelach, mogą być przekazywane jako argumenty i mogą być zwracane przez inne funkcje.

Aby to zademonstrować, utworzymy również funkcję „połowy”:

half = function(x)
    return x / 2
end

Tak więc teraz mamy dwie zmienne, half i double , obie zawierające funkcję jako wartość. Co jeśli chcielibyśmy stworzyć funkcję, która wprowadziłaby liczbę 4 do dwóch podanych funkcji i obliczyła sumę obu wyników?

Będziemy chcieli wywołać tę funkcję jak sumOfTwoFunctions(double, half, 4) . Spowoduje to dodanie funkcji double funkcji half i liczby całkowitej 4 do naszej własnej funkcji.

function sumOfTwoFunctions(firstFunction, secondFunction, input)
    return firstFunction(input) + secondFunction(input)
end

Powyższa funkcja sumOfTwoFunctions pokazuje, w jaki sposób funkcje mogą być przekazywane w obrębie argumentów i dostępne pod inną nazwą.

Parametry domyślne

function sayHello(name)
    print("Hello, " .. name .. "!")
end

Ta funkcja jest prostą funkcją i działa dobrze. Ale co by się stało, gdybyśmy po prostu 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 ?

To nie do końca świetnie. Istnieją dwa sposoby rozwiązania tego problemu:

  1. Natychmiast wracasz z funkcji:

    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
    
  2. Ustawiasz parametr domyślny .

    Aby to zrobić, po prostu użyj tego prostego wyrażenia

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

Idiom name = name or "Jack" działa, ponieważ or w zwarciach Lua. Jeśli pozycja po lewej stronie or jest inna niż nil lub false , to prawa strona nigdy nie jest oceniana. Z drugiej strony, jeśli sayHello zostanie wywołany bez parametru, name będzie nil , a więc ciąg "Jack" zostanie przypisany do name . (Należy zauważyć, że ten idiom zatem nie będzie działał, jeśli wartość logiczna false jest rozsądną wartością dla danego parametru).

Wiele wyników

Funkcje w Lua mogą zwracać wiele wyników.

Na przykład:

function triple(x)
    return x, x, x
end

Podczas wywoływania funkcji, aby zapisać te wartości, musisz użyć następującej składni:

local a, b, c = triple(5)

Co spowoduje, że w tym przypadku a = b = c = 5 . Możliwe jest również zignorowanie zwracanych wartości za pomocą zmiennej odchylnej _ w żądanym miejscu na liście zmiennych:

local a, _, c = triple(5)

W takim przypadku druga zwracana wartość zostanie zignorowana. Można również zignorować zwracane wartości, nie przypisując ich do żadnej zmiennej:

local a = triple(5)

Zmienna a otrzyma pierwszą wartość zwracaną, a pozostałe dwie zostaną odrzucone.

Gdy funkcja zwraca zmienną liczbę wyników, można je wszystkie zapisać w tabeli, wykonując funkcję w niej:

local results = {triple(5)}

W ten sposób można iterować tabelę results aby zobaczyć, co funkcja zwróciła.

Uwaga

W niektórych przypadkach może to być niespodzianka, na przykład:

local t = {}
table.insert(t, string.gsub("  hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)

Dzieje się tak, ponieważ string.gsub zwraca 2 wartości: podany ciąg, z zastąpionymi wystąpieniami wzorca i całkowitą liczbą dopasowań, które wystąpiły.

Aby rozwiązać ten problem, użyj zmiennej pośredniej lub put () wokół wywołania, tak jak poniżej:

table.insert(t, (string.gsub("  hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}

Pobiera to tylko pierwszy wynik połączenia i ignoruje resztę.

Zmienna liczba argumentów

Argumenty Variadic

Nazwane argumenty

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

Sprawdzanie typów argumentów

Niektóre funkcje działają tylko na określonym typie argumentu:

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

ma to kilka implikacji:

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"

teraz możemy wywołać funkcję z dowolnym parametrem i nie spowoduje to awarii programu.

-- 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

A co jeśli mamy funkcję, która robi coś z instancją określonej klasy? Jest to trudne, ponieważ klasy i obiekty są zwykle tabelami, więc funkcja type zwróci '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

Rozwiązanie: porównaj metatable

-- 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

Domknięcia

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

typowy przykład użycia

function new_adder(number)
    return function(input)
        return input + number
    end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5

bardziej zaawansowany przykład użycia

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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow