Lua является типичным процедурным языком программирования. Он предоставляет широкие возможности для объектно-ориентированной и функциональной разработки. Lua создавался как мощный и простой язык, обладающий всеми необходимыми выразительными средствами. Библиотека функций языка Lua написанна на языке С (точнее на подмножестве ANSI C и C++).
Будучи расширяемым языком, Lua не имеет понятия " main " программы: он работает в среде исполнения, сокращенно называемой хост. Хост-программа позволяет запускать части кода, написанные на Lua, модифицировать переменные Lua и регистрировать С-функции для использования непосредственно в коде Lua. Благодаря возможности расширения с помощью С-функций, Lua может применяться для решения широкого круга задач. Таким образом, мы имеем возможность создавать специализированные библиотеки, использование которых ничем не отличается от использования стандартных средств языка. В поставку включена простая хост-программа lua, которая использует библиотеку Lua и представляет из себя полную и автономную реализацию интерпретатора языка.
Lua является свободно распространяемым программным средством, поэтому предоставляется без каких либо гарантий в соответствие с лицензией. Версия lua 5.1, которая описана в данном Руководстве, доступна на официальном сайте Lua www.lua.org
Как и многие подобные Руководства, этот документ написан в формальном стиле. Для получения более подробной информации об особенностях применения языка рекомендуем обратиться к технической документации, доступной на официальном сайте Lua. Хорошим подспорьем в работе может оказаться книга Роберта Иерусалимского (Roberto Ierusalimschy) «Программирование на Lua», второе издание (Programming in Lua (Second Edition)).
Именами (идентификаторами) в Lua могут быть любые строки из букв, цифр и символа подчеркивания, не начинающиеся с цифры. Это правило типично для большинства языков программирования. (Понятие буквы зависит от текущей локали: любой символ из алфавита текущей локали может быть использован в составе идентификатора). Идентификаторы используются для именования переменных и таблиц значений (table fields).
Следующие ключевые слова зарезервированы и не могут быть использованы в именах:
and break do else elseif end false for function if in local nill not or repeat return then true until while Lua является языком, чувствительным к регистру символов: and – ключевое слово, тогда как And и AND– два разных допустимых идентификатора. По соглашению, имена, начинающиеся с символа подчеркивания и записанные в верхнем регистре (например _VERSION), зарезервированы для использования в качестве внутренних глобальных переменных, используемых Lua.
В следующих строках показаны другие допустимые символы:
+ * / % ^ < # == ~= <= >= < > = ( ) { } [ ]> ; : , . .. ... Литеральные строки должны быть заключены в одинарные или двойные кавычки и могут содержать следующие С-подобные escape-поледовательности: '\a' («звонок»), '\b' («забой»), '\f' («перевод страницы»), '\n' («перевод на новую строку»), '\r' («возврат каретки»), '\t' («горизонтальная табуляция»), '\v' («вертикальная табуляция»), '\\\"' («двойная кавычка»), and'\'' (апостроф [«одинарная кавычка»]). Кроме того, обратный слеш ставится перед концом строки в редакторе, когда для удобства набора длинные непрерывные строки записываются в несколько строк. Символ в строке также может быть представлен своим кодом с помощью escape-последовательности \ddd, где ddd- последовательность из не более чем трех цифр. (Заметим, что если после символа, записанного с помощью своего кода, должна идти цифра, то код символа в escape-последовательности должен содержать ровно три цифры). Строки в Lua могут содержать любые 8-битные значения, включая ноль, который записывается как '\0'.
To put a double (single) quote, a newline, a backslash, or an embedded zero inside a literal string enclosed by double (single) quotes you must use an escape sequence. Any other character may be directly inserted into the literal. (Some control characters may cause problems for the file system, but Lua has no problem with them.)
Literal strings can also be defined using a long format enclosed by long brackets. We define an opening long bracket of level n as an opening square bracket followed by n equal signs followed by another opening square bracket. So, an opening long bracket of level 0 is written as [[, an opening long bracket of level 1 is written as [=[, and so on. A closing long bracket is defined similarly; for instance, a closing long bracket of level 4 is written as ]====]. A long string starts with an opening long bracket of any level and ends at the first closing long bracket of the same level. Literals in this bracketed form may run for several lines, do not interpret any escape sequences, and ignore long brackets of any other level. They may contain anything except a closing bracket of the proper level.
For convenience, when the opening long bracket is immediately followed by a newline, the newline is not included in the string. As an example, in a system using ASCII (in which 'a' is coded as 97, newline is coded as 10, and '1' is coded as 49), the five literals below denote the same string:
a = ' alo\n123"' a = "alo\n123\"" a = '\97lo\10\04923"' a = [[alo 123"]] a = [==[ alo 123"]==] A numerical constant may be written with an optional decimal part and an optional decimal exponent. Lua also accepts integer hexadecimal constants, by prefixing them with 0x. Examples of valid numerical constants are
3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x56 A comment starts with a double hyphen (--) anywhere outside a string. If the text immediately after -- is not an opening long bracket, the comment is a short comment, which runs until the end of the line. Otherwise, it is a long comment, which runs until the corresponding closing long bracket. Long comments are frequently used to disable code temporarily.
Lua представляет собой язык с динамическим определением типов данных. Переменная языка может содержать значения любого типа. Возможности определения пользовательских типов данных отсутствуют. Все значения в Lua могут храниться в переменных, использоваться в качестве аргументов при вызове функций и возвращаться в виде результата их выполнения.
В Lua восемь основных типов: nil (неопределенный), boolean (логический), number (числовой), string (строковый), function (функция), userdata (пользовательские данные), thread (поток), и table (таблица). Nil - это тип значения nil [пустое значение], главное свойство которого – отличаться от всех остальных значений и обозначать отсутствие пригодного значения. К типу Boolean относятся значения false (ложь) и true (истина). Значения nil и false считаются ложными, любое другое значение считается истинным. К типу Number относятся вещественные числа (двойной точности с плавающей запятой). (Легко можно сделать сборку интерпретатора Lua с другим внутренним представлением чисел, достаточно изменить определение в файле luaconf.h.). Тип String обозначает массивы символов. Строки Lua могут содержать любые 8 битные символы, включая ноль ('\0') (смотреть §2.1).
В Lua можно использовать функции, написанные на Lua и на C (смотреть §2.5.8).
Тип userdata (пользовательские данные) позволяет хранить любые данных из C в переменных Lua. Значение этого типа является ссылкой на блок физической памяти и не имеет предопределенных операций в Lua, за исключением присваивания и проверки на равенство. Однако, используя метатаблицы, програмист может определить операции над значениями этого типа (смотрите §2.8). Значения типа userdata не могут быть созданы или изменены непосредственно в Lua, это возможно только с помощью C API. Такой подход гарантирует целостность данных, принадлежащих ведущей программе.
Тип thread (поток) обозначает независимый поток исполнения и используется при реализации механизма сопрограмм (смотрите §2.11). Нельзя отождествлять потоки Lua с потоками операционной системы. Lua поддерживает подпрограммы даже в тех системах, где потоки на уровне операционной системы не поддерживаются.
Тип table (таблица) определяет ассоциативные массивы. Такие массивы могут индексироваться не только числами, но и любыми значениями (за исключением nil). Таблица может содержать значения сразу нескольких типов (кроме nil). Таблицы представляют собой единственный механизм структурирования данных в Lua; они могут использоваться как простые массивы, таблицы сиволов, множества, поля записей, деревья и так далее. Для представления словарей Lua использует имя поля в качестве индекса таблицы. Представление в виде a.name считается тождественным представлению a["name"]. В Lua есть несколько способов создания таблиц (смотреть §2.5.7).
Индексы и значения полей таблицы могут быть любого типа (кроме nil). В частности, так как функции являются значениями встроенного типа, поля таблицы могут содержать и функции. Таким образом, таблицы могут хранить методы methods (смотреть §2.5.9).
Переменные типа table, function, thread и userdata не содержат самих данных, в них хранятся только ссылки на соответствующий объект. Присваивание, передача параметров и возврат результата из функции оперируют только ссылками на значения, эти операции никогда не ведут к созданию копий.
Библиотечная функция type возвращает строку, описывающую тип данного значения.
Переменные используются для хранения значений в процессе выполнения программы. В Lua есть три вида переменных: глобальные, локальные и поля таблиц.
Отдельный идентификатор может обозначать глобальную или локальную переменную (либо формальный параметр функции, что является частным случаем локальной переменной) :
var ::= Name Имя
Где Name – идентификатор, определяемый в соответствии с §2.1.
Любая переменная считается глобальной, если она явно не объявлена как локальная (смотрите §2.4.7). Локальные переменные существуют в лексическом контексте: локальные переменные доступны функциям, определенным внутри этого контекста (смотрите §2.6).
До первого явного присвоения значением переменной является nil.
Квадратные скобки используются для доступа к элементу таблицы по индексу:
var ::= prefixexp '[' exp ']'
Способ доступа к глобальным переменным и полям таблицы может быть изменен с помощью мететаблиц. Доступ к переменной t[i] эквивалентен вызову gettable_event(t,i). (Полное описание функции gettable_event смотрите в §2.8. Эта функция недоступна в коде Lua, мы упоминули ее здесь в качестве примера).
Запись var.Name аналогична записи var["Name"]:
var ::= prefixexp '.' Name
Все глобальные переменные являются полями в обычных таблицах Lua, называемых таблицами окружения или кратко окружениями (смотреть §2.9). Каждая функция имеет ссылку на свое собственное окружение, и все глобальные переменные внутри этой функции ссылаются на данную таблиц. В момент создания функция наследует окружение вызывающей функции. Для получения таблицы окружения функии Lua можно вызвать функцию getfenv. Для перезаписи таблицы используется setfenv. (Вы можете манипулировать окружением C функций только с помощью отладочной библиотеки (смотрите §5.9).)
Обращение к глобальной переменной x эквивалентно _env.x, а также
gettable_event(_env, "x")
где _env – окружение выполняющейся функции. (Полное описание функции gettable_event смотрите в §2.8 . Эта функция недоступна в коде Lua, мы упоминули ее здесь в качестве примера).
Числа и символьные строки рассмотрены в §2.1; переменные - в §2.3; описания функций - в §2.5.9; вызовы функций - в §2.5.8; конструкторы таблиц - в §2.5.7. Неявные аргументы, обозначаемые ‘...', могут использоваться только внутри соответственно заданной функции; смотрите §2.5.9.
К бинарным операциям (binop в формальном определении выражения) относятся арифметические (смотрите §2.5.1), операции сравнения (§2.5.2), булевские (§2.5.3) и операции конкатенации (смотерть §2.5.4). Унарными являются унарный минус (§2.5.1), отрицание not (§2.5.3) и операция получения длины # (§2.5.5).
Результат вызова функций и неявные параметры могут содержать несколько значений. Если при этом они используются в качестве оператора (§2.4.6) (только для функций), то все возвращеннаемые значения отбрасываются. Если это последний (или единственный) элемент в списке выражений, то никакая корректировка не проводится (если вызов не взят в скобки). В остальных случаях Lua приводит возвращаемый список к одному элементу, отбрасывая все значения кроме первого.
Далее несколько примеров:
f() -- результат функции отбрасывается
g(f(), x) -- берется первое значение из списка - результата вызова f()
g(x, f()) -- g получает x и все значения, полученные из f()
a,b,c = f(), x -- берется первый элемент результата вызова f()(и c получает nil)
a,b = ... -- a получает первый параметр из ..., b - второй (причем а и b могут получить nil, если в качестве неявных параметров ничего не передано)
a,b,c = x, f() -- 2 результата из f()
a,b,c = f() -- 3 результата из f()
return f() -- возвращает все значения из f()
return ... -- возвращает все полученные неявные аргументы
return x,y,f() -- вернет a, b и все, что вернет f()
{f()} -- создаст список со всем результатами вызова f()
{...} -- создаст список со всеми неявными параметрами
{f(), nil} -- 1 результат из f()
Выражение, заключенное в скобки всегда возвращает только одно значение. Таким образом, (f(x,y,z)) всегда даст единственное значение, даже если f возвращает несколько. (Значение (f(x,y,z)) это первое значение, полученное из f, или nil, если f не возвращает значений.)
Lua язык с лексическим разграничением областей видимости. Область видимости переменной начинается первым выражением после ее объявления и действует до конца блока, в котором это объявление встречается. Рассмотрим следующий пример:
x = 10 -- глобальная переменная variable
do -- начало блока
local x = x -- объявление локальной переменной
print(x) --> 10
x = x+1
do -- начало вложенного блока
local x = x+1 -- другая локальная 'x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (глобальная переменная)
Отметим, что в объявлении local x = x локальная переменная объявляется еще не в области своей видимости, поэтому присваивается именно внешняя переменная.
В соответствии с правилами лексического разграничения областей видимости, локальные переменные доступны в функциях, определенных внутри их области видимости. Локальная переменная, используемая в таких функциях, называется внешней локальной переменной (по отношению к определенной внутри ее области видимости функции).
Обработка каждого объявления local ведет к созданию новой локальной переменной. Рассмотрим следующий пример:
a = {}
local x = 20
for i=1,10 do
local y = 0
a[i] = function () y=y+1; return x+y end
end
Цикл создает 10 экземпляров функции, в которых используются различные переменные y и один и тот же x.
Поскольку Lua является языком расширений, работа Lua начинаются с момента вызова в C-коде базовой программы функции из Lua-библиотеки (lua_pcall). При возникновении ошибки в процессе компиляции или выполнения Lua управление возвращается в C -программу, где и осуществляется ее обработка (например вывод сообщения о ошибке).
Lua-код может явно генерировать ошибку, вызывая функцию error. Если вам нужно перехватывать ошибки в самом Lua, вы можете использовать функцию pcall.
Любое значение в Lua может иметь метатаблицу. Метатаблица – это обычная таблица Lua, в которой определены допустимые операции над значением. Вы можете изменить действие некоторых операции над значением путем задания соответствующих полей в его метатаблице. Для экземпляров классов, например когда к нечисловым значениям применяется операция сложения, Lua ищет реализацию этой операции в поле "__add" его метатаблицы. Если реализация найдена, Lua запускает эту операцию для выполнения сложения.
Ключевые поля в метатаблице мы называем событиями, а значения - метаметодами. В рассмотреном примере событием является "add", а метаметодом является функция сложения.
Получить метатаблицу любого значения можно с помощью функции getmetatable.
Вы можете заменить метатаблицу с помощью функции setmetatable. Вы не можете изменить метатаблицу другим способом (of other types – других типов???) (за исключением случаев использования отладочной библиотеки), для этого требуется воспользоваться C API.
Таблицы и пользовательские данные имеют индивидуальные метатаблицы (в то же время множества таблиц и пользовательских данных может совместно использовать соответствующие метатаблицы). Переменные всех других типов совместно используют одну метатаблицу на тип. То есть есть одна метатаблица на все числовые значения, одна на все строки и т.д.
В метатаблице могут быть заданы правила выполнения арифметических операций над объектом, порядок сравнения, конкатенации, способ вычисления длины и индексирования. Также в метатаблице может быть определена функция «сборки мусора». Для каждой из этих операций в Lua определен специальный ключ, называемый событие. В момент, когда Lua выполняет одну из этих операций над значением, проверяется, есть ли в метатаблице значение с соответствующим событием. Если оно найдено, значение по этому ключу (метаметод) и определяет способ выполнения операции.
Далее рассмотрим управление операциями с помощью метатаблиц. Каждая операция идентифицируется по своему имени. Ключ каждой операции представляет из себя строку из имени и двух нижних подчеркиваний перед ним. Для экземпляра – ключом операции “add” будет строка "__add". Для лучшего понимания мы в терминах Lua покажем запуск операции интерпретатором.
Представленный здесь код на Lua приведен в качестве иллюстрации, реальный код интерпретатора гораздо сложнее и эффективнее этого схематичного примера. Все функции, использованные в этом примере (rawget, tonumber и т.п.), описаны в §5.1. Вообще говоря, для получения метаметода объекта мы используем конструкцию
metatable(obj)[event]
Это следует читать как
rawget(getmetatable(obj) or {}, event)
Таким образом, при получении доступа к метаметоду другие метаметоды не используются, поэтому доступ к объекту без метатаблицы не приводит к ошибке (мы просто получим nil).
· "add": операция сложения.
Функция getbinhandler ниже показывает, каким образом Lua получает указатель на операцию. Во-первых, Lua пытается получить первый операнд. Если его тип не является типом указателя на операцию, Lua пытается получить второй операнд.
function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end
С помощью этой функции поведение конструкции op1 + op2 выглядит как
function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- операнды являются числами?
return o1 + o2 -- '+' здесь – стандартная операция сложения
else -- когда хотя бы один из операндов нечисловой
local h = getbinhandler(op1, op2, "__add")
if h then
-- вызов функции с операндами по указателю
return h(op1, op2)
else -- указатель не найден: обработка ошибки
error(···)
end
end
end
· "sub": операция ‘ - ‘. Обработка аналогична "add".
· "mul": операция ‘ * ’. Обработка аналогична "add".
· "div": операция ‘ / ’ . Обработка аналогична “add”.
· "mod": операция ‘ % ’. Обработка похожа на "add", только o1 - floor(o1/o2)*o2 подставляется вместо стандартной операции.
· "pow": операция ‘ ^ ’. Обработка похожа на "add", вместо стандартной операции подставляется функция pow (из математической библиотеки C).
· "unm": операция «унарный минус».
function unm_event (op)
local o = tonumber(op)
if o then -- операнд является числом?
return -o -- '-' здесь – стандартый оператор
else -- операнд нечисловой.
-- Попытка получить указатель на функцию по операнду
local h = metatable(op).__unm
if h then
-- вызов функции с операндом на входе
return h(op)
else -- указатель не найден: обработка ошибки
error(···)
end
end
end
· "concat": Операция конкатенации ‘..’
function concat_event (op1, op2)
if (type(op1) == "string" or type(op1) == "number") and
(type(op2) == "string" or type(op2) == "number") then
Смотрите §2.5.5 с описанием функции вычисления длины таблицы.
· " eq ": операция ‘= =’. Функция getcomphandler определяет, как выбирает метаметод для сравнения операторов. Метаметод вызывается, только если сравниваются объекты одного типа, и метаметоды объектов для этой операции равны.
function getcomphandler (op1, op2, event)
if type(op1) ~= type(op2) then return nil end
local mm1 = metatable(op1)[event]
local mm2 = metatable(op2)[event]
if mm1 == mm2 then return mm1 else return nil end
end
Событие "eq" определяется следующим образом:
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- типы различны?
return false -- объекты не равны
end
if op1 == op2 then -- стандартное сравнение истинно?
return true -- объекты равны
end
-- попытка получения метаметода
local h = getcomphandler(op1, op2, "__eq")
if h then
return h(op1, op2)
else
return false
end
end
a ~= b означает отрицание (a == b).
· "lt": операция ‘<’.
function lt_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 < op2 -- числовое сравнение
elseif type(op1) == "string" and type(op2) == "string" then
return op1 < op2 -- лексическое сравнение
else
local h = getcomphandler(op1, op2, "__lt")
if h then
return h(op1, op2)
else
error(···);
end
end
end
a > b эквивалентно b < a. · "le": операция ‘ <= ’.
function le_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 <= op2 -- числовое сравнение
elseif type(op1) == "string" and type(op2) == "string" then
return op1 <= op2 -- лексическое сравнение
else
local h = getcomphandler(op1, op2, "__le")
if h then
return h(op1, op2)
else
h = getcomphandler(op1, op2, "__lt")
if h then
return not h(op2, op1)
else
error(···);
end
end
end
end
a >= b эквивалентно b <= a. Заметим, что при отсутствии метаметода "le" , Lua пытается применить метаметод "lt", полагая, что a <= b эквиваленто отрицанию (b < a.
· "index": Доступ по индексу table[key].
function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then return v end
h = metatable(table).__index
if h == nil then return nil end
else
h = metatable(table).__index
if h == nil then
error(···);
end
end
if type(h) == "function" then
return h(table, key) -- вызов по указателю
else return h[key] -- или рекурсивный вызов операции
Кроме метатаблиц, объекты типа thread, function и userdata обладают дополнительными таблицами, которые называют окружением. Подобно метатаблицам, окружения являются регулярными таблицами и множество объектов могут совместно использовать одно окружение.
Окружение, соответствующее типу userdata, не имеет смысла в Lua. Оно введено для удобства ассоциирования таблиц и значений типа userdata.
Окружения, относящиеся к типу threads, называют глобальными окружениями. Они используются как окружения по умолчанию для потоков и невложенных функций, созданных в потоке (с помощью loadfile, loadstring или load) и могут использоваться непосредственно в С-коде (смотрите §3.3).
Окружения, ассоциированные с C -функциями, могут непосредственно использоваться в C -коде (смотрите §3.3). Они используются как окружения по умолчанию для других С-функций, созданных на базе этой.
Окружения, ассоциированные со стандартными функциями Lua, используются для доступа к глобальным переменным из любой функции (смотрите §2.3). Они используются по умолчанию для других функций Lua, построенных на базе этих..
Вы можете изменять окружение функций Lua или запупскать потоки при помощи вызова setfenv. Вы можете получить окружение Lua -функции или запущенного потока с помощью getfenv . Доступ к окружению других объектов (типа userdata, функций C, потоков) возможен посредством C API.
Lua осуществляет автоматичекое управление памятью. Это означает, что вам не нужно думать о выделении памяти при создании новых объектов и ее освобождении, когда объект становится ненужным. Lua время от времени автоматически запускает процедуру сборки мусора для удаления устаревших объектов (то есть объектов, которые более недоступны из Lua). Сборщик мусора обрабатывает все объекты Lua: таблицы, данные типа userdata, функции, потоки и строки.
В Lua реализован инкрементный сборщик по принципу пометить-очистить. Цикл работы сборщика мусора зависит от двух параметров: пауза сборки мусора и коэффициент шага сборки.
Паузой определяется время между запусками циклов сборки. Большие значения этого параметра делают сборку мусора менее активной. Значения меньше 1 означают, что между запусками циклов сборки паузы нет. При значении 2 сборщик перед следующим запуском ждет удвоения объема использованой памяти.
Коэффициент шага сборки управляет скоростью сборки в зависимости от интенсивности выделения памяти. Большие значения параметра ускоряют работу сборщика, но при этом увеличивается размер каждого шага. Larger values make the collector more aggressive but also increase the size of each incremental step.Значения меньше 1 делают сборщик медленным и могут привести к тому, что цикл сборки никогда не закончится. По умолчанию используется значение 2, в этом случае сборщик работает вдвое быстрее процесса выделения памяти.
Вы можете менять эти параметры посредством вызова lua_gc в C или collectgarbage в Lua. В обоих случаях в качестве аргументов берутся проценты (т.е. аргумент 100 означает значение параметра 1).Этими функциями вы можете непосредственно управлять сборкой (например, останавливать ее или рестартовать).
Lua поддерживает подпрограммы, эту технологию часто называют общей многопоточностью. Подпрограмма Lua представляет собой независимый поток выполнения. Несмотря на это, в отличие от потоков в традиционных многопоточных системах, подпрограмма может приостановить свое выполнение только в результате явного вызова функции yield.
Подпрограммы создаются вызовом coroutine.create. Единственным аргументом является имя главной функции подпрограммы. Функция create только создает новую подпрограмму и возвращает указатель на нее (объект типа thread), запуск подпрограммы не выполняется.
При вызове функции coroutine.resume и передаче ей в качестве первого аргумента результата вызова coroutine.create, процедура запускается на выполнение с первого оператора ее главной функции. Остальные параметры из вызова coroutine.resume передаются в основную функцию подпрограммы. После запуска подпрограмма выполняется до завершения либо до вызова yields.
Подпрограмма останавливается только в двух случаях: нормально, когда осуществляется возврат (явно или неявно) из главной функции; или аварийно в случае необработанной ошибки. При нормальном завершении coroutine.resume возвращает true плюс любые значения, возвращаемые из основной функции подпрограммы. В случае ошибок, coroutine.resume вернет значение false плюс сообщение об ошибке.
Для приостановки выполнения попрограммы используется функция coroutine.yield. При вызове yields соответствующий coroutine.resume возвращает управление немедленно, точно так же, как если бы вызов yield произошел во вложенном вызове функции (т.е. не в главной функции, а в функции, вызванной непосредственно или опосредованно из нее). При вызове yield функция coroutine.resume также возвращает true плюс все входные параметры, переданные в coroutine.yield. В следующий раз, когда подпрограмма продолжит работу, ее выполнение начнется с оператора, следующего за yield, соответственно из coroutine.yield вернутся параметры, переданные в coroutine.resume.
Функция coroutine.wrap создает подпрограмму и осуществляет ее запуск. Параметры, переданные в нее в качестве дополнительных аргуметров, попадают в неявный вызов coroutine.resume. Вызов coroutine.wrap возвращает те же значения, что и coroutine.resume, за исключением первого (булевского кода ошибки). В отличие от coroutine.resume, coroutine.wrap не перехватывает ошибки – все ошибки попадают на уровень вызывающей функции.
Рассмотрим в качестве примера следующий код:
function foo (a) print("foo", a) return coroutine.yield(2*a) end
co = coroutine.create(function (a,b) print("co-body", a, b) local r = foo(a+1) print("co-body", r) local r, s = coroutine.yield(a+b, a-b) print("co-body", r, s) return b, "end" end)
print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x", "y"))
При запуске на экран выведется:
co-body 1 10 foo 2 main true 4 co-body r main true 11 -9 co-body x y main true 10 end main false cannot resume dead coroutine