Поиск…


Синтаксис

  • funcname = function (paramA, paramB, ...) body; return exprlist end - простая функция
  • function funcname (paramA, paramB, ...) body; return exprlist end - сокращенное обозначение выше
  • local funcname = function (paramA, paramB, ...) body; return exprlist end - лямбда
  • local funcname ; funcname = function (paramA, paramB, ...) body; return exprlist end - lambda, который может выполнять рекурсивные вызовы
  • локальная функция funcname (paramA, paramB, ...) body; return exprlist end - сокращенное обозначение выше
  • funcname (paramA, paramB, ...) - вызов функции
  • local var = var или «Default» - параметр по умолчанию
  • return nil, «сообщения об ошибках» - стандартный способ прерывания с ошибкой

замечания

Функции обычно устанавливаются с function a(b,c) ... end и редко с установкой переменной на анонимную функцию ( a = function(a,b) ... end ). Противоположное верно при передаче функций в качестве параметров, в основном используются анонимные функции, а обычные функции не используются так часто.

Определение функции

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

Давайте посмотрим на синтаксис. Во-первых, мы видим ключевое слово function . Ну, это довольно описательно. Затем мы видим идентификатор add ; имя. Затем мы видим аргументы (a, b) они могут быть любыми, и они являются локальными. Только внутри тела функции мы можем получить к ним доступ. Давайте перейдем к концу, мы увидим ... ну, end ! И все, что находится между ними, - это тело функции; код, который запускается при его вызове. Ключевое слово return - это то, что заставляет функцию давать полезный результат. Без него функция ничего не возвращает, что эквивалентно возврату nil. Разумеется, это может быть полезно для вещей, которые взаимодействуют с IO, например:

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

В этой функции мы не использовали оператор return.

Функции также могут возвращать значения условно, что означает, что функция имеет выбор возврата ничего (nil) или значения. Это показано в следующем примере.

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

Также возможно, чтобы функция возвращала несколько значений, разделенных запятыми, как показано:

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

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

Функции также могут быть объявлены локальными

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

Они также могут быть сохранены в таблицах:

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

Вызов функции.

Функции полезны, если мы можем их назвать. Для вызова функции используется следующий синтаксис:

print("Hello, World!")

Мы вызываем функцию print . Используя аргумент "Hello, World" . Как очевидно, это приведет к выводу Hello, World в выходной поток. Возвращаемое значение доступно, как и любая другая переменная.

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

Переменные также принимаются в параметрах функции.

local a = 10
local b = 60

local c = add(a, b)

print(c)

Функции, ожидающие, что таблица или строка могут быть вызваны с помощью чистого синтаксического сахара: круглые скобки, окружающие вызов, могут быть опущены.

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

Анонимные функции

Создание анонимных функций

Анонимные функции похожи на обычные функции Lua, за исключением того, что у них нет имени.

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

Как вы можете видеть, функция не назначается ни одному имени, как print или add . Чтобы создать анонимную функцию, все, что вам нужно сделать, это опустить имя. Эти функции также могут принимать аргументы.

Понимание синтаксического сахара

Важно понимать, что следующий код

function double(x)
    return x * 2
end

на самом деле является просто сокращением для

double = function(x)
    return x * 2
end

Однако вышеуказанная функция не является анонимной, поскольку функция напрямую привязана к переменной!

Функции - это значения первого класса

Это означает, что функция является значением с теми же правами, что и обычные значения, такие как числа и строки. Функции могут храниться в переменных, в таблицах, могут передаваться как аргументы и могут быть возвращены другими функциями.

Чтобы продемонстрировать это, мы также создадим «половину» функции:

half = function(x)
    return x / 2
end

Итак, теперь у нас есть две переменные, half и double , обе из которых содержат функцию как значение. Что делать, если мы хотим создать функцию, которая будет подавать число 4 на две заданные функции и вычислять сумму обоих результатов?

Мы будем называть эту функцию, например sumOfTwoFunctions(double, half, 4) . Это будет half функцию double функцию half и целое число 4 в нашей собственной функции.

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

Вышеуказанная функция sumOfTwoFunctions показывает, как функции могут передаваться внутри аргументов и доступны другим именем.

Параметры по умолчанию

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

Эта функция является простой функцией, и она работает хорошо. Но что произойдет, если мы просто 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 ?

Это не совсем здорово. Это можно сделать двумя способами:

  1. Вы сразу возвращаетесь из функции:

    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. Вы устанавливаете параметр по умолчанию .

    Для этого просто используйте это простое выражение

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

Идиома name = name or "Jack" работает, потому что or в коротких замыканиях Lua. Если элемент с левой стороны or есть что-то другое, кроме nil или false , тогда правая сторона никогда не будет оценена. С другой стороны, если sayHello вызывается без параметра, тогда name будет равно nil , и поэтому строка "Jack" будет назначена для name . (Обратите внимание, что эта идиома, следовательно, не будет работать, если логическое false является разумным значением для рассматриваемого параметра.)

Несколько результатов

Функции в Lua могут возвращать несколько результатов.

Например:

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

При вызове функции, чтобы сохранить эти значения, вы должны использовать следующий синтаксис:

local a, b, c = triple(5)

Это приведет a = b = c = 5 в этом случае a = b = c = 5 . Также можно игнорировать возвращаемые значения, используя переменную throwaway _ в нужном месте в списке переменных:

local a, _, c = triple(5)

В этом случае второе возвращаемое значение будет проигнорировано. Также можно игнорировать возвращаемые значения, не присваивая их любой переменной:

local a = triple(5)

Переменной a будет присвоено первое возвращаемое значение, а остальные два будут отброшены.

Когда переменная сумма результатов возвращается функцией, их можно хранить в таблице, выполняя внутри нее функцию:

local results = {triple(5)}

Таким образом, можно выполнить итерацию по таблице results чтобы увидеть, как вернулась функция.

Заметка

В некоторых случаях это может быть неожиданностью, например:

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

Это происходит потому, что string.gsub возвращает 2 значения: заданную строку, с заменой шаблонов и общее число совпадений.

Чтобы решить эту проблему, используйте либо промежуточную переменную, либо put () вокруг вызова, например:

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

Это захватывает только первый результат вызова и игнорирует остальные.

Переменная количество аргументов

Вариадические аргументы

Именованные аргументы

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

Проверка типов аргументов

Некоторые функции работают только с определенным типом аргумента:

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

это имеет несколько последствий:

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"

теперь мы можем вызвать функцию с любым параметром, который мы хотим, и это не приведет к сбою программы.

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

Итак, что, если у нас есть функция, которая что-то делает с экземпляром определенного класса? Это сложно, потому что классы и объекты обычно являются таблицами, поэтому функция type вернет '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

Решение: сравните метатеги

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

Затворы

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

типичный пример использования

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

более продвинутый пример использования

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow