Поиск…
Синтаксис
- 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 ?
Это не совсем здорово. Это можно сделать двумя способами:
Вы сразу возвращаетесь из функции:
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
Вы устанавливаете параметр по умолчанию .
Для этого просто используйте это простое выражение
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