Материал о Lua взят с сайта ltwood.wikidot.com.
Это руководство претендует на то, чтобы считаться самым полным. Несколько строк посвящено довольно простым принципам "Милены". Весь остальной объем занимает руководство по Lua и описание его стандартных библиотек. К сожалению, уроков пока не существует в природе. В дальнейшем они будут включены в это руководство в отдельной главе.
Введение
"Милена" - это интерпретатор текстовых игр с менюшным вводом(CYOA), основанный на языке Lua, предназначенный для проигрывания файлов в формате "Милена". Для того, чтобы создавать игры на "Милене" Вам не понадобится ничего кроме текстового редактора и этого руководства(если Вы читаете его в первый раз). С помощью дополнительных библиотек, которые Вы можете распространять либо со своей игрой либо как дополнение к интерпретатору, Вы свободно можете включать в игру музыку самых разных форматов, видео и другую медиа-информацию.
Оформление исходного кода и базовые понятия
В этой главе рассказывается об оформлении исходного кода (т.е. как оформить код, чтобы потом в нем не запутаться и чтобы другие люди смогли свободно его прочитать) и базовах понятиях "Милены"
Оформление исходного кода
Как оформить код, чтобы потом в нем не запутаться? Приведу пример неправильного оформления.
a=1 b=5 for i=1,10 do if a*10<b then a=a+b end end
Так код оформлять НЕ желательно. Почему? Просто потому, что в данном случае (когда конструкции всего две) еще видно где кончается каждая из них. А представьте, что у Вас пять или десять конструкций в одной строке! Если Вам понадобится внести изменения в какую-то одну конструкцию, Вам придется дольше искать ее, и не факт, что Вы не промахнетесь. Теперь посмотрим на этот же пример, но уже оформленный правильно.
a=1 b=5
for i=1,10 do
if a*10<b then
a=a+b
end
end
Здесь четко видно где начинается и кончается каждая из конструкций. Запомнив положение начала конструкции, Вы легко найдете ее конец. Кроме того, если кто-то захочет разобраться в коде Вашей игры, он быстрее все поймет. (Для тех, кто не любит, когда в его коде копаются, существует шифрование).
Шифрование
Шифрование происходит по алгоритму MD5 с секретным паролем. Вы можете скачать утилиту для шифрования и перед релизом шифровать свои игры.
Базовые понятия "Милены"
Хотя "Милена" это почти Lua(для программирования используется именно этот язык), все же в ней существуют свои базовые понятия. Их немного, так что запомнить их не составит никакого труда.
Метка
Это строка, определяющая начало локации с некоторым именем. Например:
:метка
В этом "коде" показано определении локации с именем "метка". Любая метка определяется : (двоеточием) в начале строки. После двоеточия обычно идет название метки, являющееся одновременно и названием локации, определенной меткой.
Локация
Это элементарная единица игрового мира, например комната, пещера или лесная лорога. Обычно в локации размещается описание чего-либо, но Вы можете оставить локацию пустой, либо наполнить ее только кодом. Любая локация определяется меткой и заканчивается ключевым словом endl. Каждая локация определяется единожды, хотя может быть переопределена. Посмотрим на предыдущий пример, где мы определяем уже полноценную локацию:
:метка
--Здесь содержится код локации
endl
Главный файл может не содержать локаций как таковых, но код в нем будет исполняться как код в анонимной локации. Локация может не иметь завершающего ключевого endl, тогда концом локации будет считаться конец файла.
Функции и системные переменные
Кроме базовых функций Lua "Милена" имеет свои функции, упрощающие вывод информации.
Базовые функции
В этой главе описываются базовые функции "Милены"
Функции стандартных модулей
Модуль container
Модуль dialogs
Модуль inventory
Для некоторых функций есть их аналоги, отмеченные постфиксом 2. Последний параметр этих функций - префикс. Такие функции отмечены *.
Системные переменные
Системные переменные нужны для хранения названийметок локаций, стилей и другой важной информации.
Управление стилем текста
Конечно, Вы захотите сделать в своей игре красивый вывод: раскрасить текст, сделать фоновый цвет не белым, а, например, светло-желтым или еще каким. Вот для этого и существуют две lua-таблицы, хранящие в себе стиль Вашей игры.
Таблица text
Как не сложно догадаться, эта таблица хранит стиль текста. Далее идет описание ключей таблицы text.
Таблица button
Эта таблица хранит стиль кнопки. Далее идет описание ключей таблицы button.
Простой пример игры
Чтобы Вы хотя бы в общих чертах уяснили как создавать на "Милене" игры, я приведу простой пример: в зависимости определенно выбранного действия мы либо выигрываем либо умираем. Реализуется это так:
:начало
--[[Это локация-инициализатор.
В ней обычно прописываются переменные,
имеющие какое-либо начальное значение,
типа жизни героя или добавляются предметы в другие локации,
определяется стиль текста.
Она может иметь любое название вплоть до пустой строки.
Но мы просто выводит текст.]]
pln 'Вы стоите в узкой комнатушке так, что если сделаете шаг - упретесь в противоположную стену. Две двери помечены как красная и зеленая.'
btnl('красная','Войти в красную дверь')
btnl('зеленая','Войти в зеленую дверь')
endl
:красная
--Мы определили локацию "красная"
pln 'Вы опрометчиво шагнули... в пропасть.'
--И вывели неутешительный текст
endl
:зеленая
--Мы определили локацию "зеленая"
pln 'Вы уже приготовились к падению, как вдруг почувствовали под собой твердую землю!'
--И обрадовали игрока
endl
Введение
Lua — язык программирования, предназначенный для встраивания в другие приложения чтобы дать их пользователям возможность писать конфигурационные скрипты и высокоуровневые сценарии. Lua поддерживает процедурный, объектный и функциональный стили программирования, но является в то же время простым языком. Интерпретатор Lua написан на ANSI-C и представляет собой библиотеку, которую можно подключить к любой программе. В этом случае управляющая программа может вызвать библиотечные функции для выполнения участка кода на Lua и работы с данными, определенными в этом коде. Также управляющая программа может регистрировать собственные функции таким образом, что их можно будет вызывать из кода на Lua. Последняя возможность позволяет использовать Lua как язык, который можно адаптировать к произвольной области применения. Другое применение Lua — написание простых независимых скриптов. Для этой цели имеется простой интерпретатор Lua, использующая эту библиотеку для выполнения кода, вводимого с консоли или из файла.
Лексические соглашения
Идентификаторы могут содержать буквы, цифры и символы подчеркивания и не могут начинаться с цифры.
Идентификаторы, начинающиеся с подчеркивания и состоящие только из заглавных букв, зарезервированы для внутреннего использования интерпретатором.
В идентификаторах различается верхний и нижний регистры букв.
Строковые литералы можно заключать в одинарные или двойные кавычки. В них можно использовать следующие специальные последовательности символов:
\n | перевод строки (LF = 0x0a) | \a | bell |
\r | возврат каретки (CR = 0x0d) | \b | backspace |
\t | табуляция | \f | form feed |
\\ | символ обратной косой черты | \v | вериткальная табуляция |
\" | кавычка | \[ | левая квадратная скобка |
\' | апостроф | \] | правая квадратная скобка |
\ddd | символ с кодом ddd (десятичным) | \0 | символ с кодом 0 |
Если в конце строки исходного файла стоит символ обратной косой черты, то на следующей строке может быть продолжено определение строкового литерала, в который в этом месте вставится символ новой строки.
Строковые литералы можно также заключать в двойные квадратные скобки [[….]]. В этом случае литерал может быть определен на нескольких строках (символы перевода строки включаются в строковый литерал) и в нем не интерпретируются специальные последовательности символов.
Если непосредственно после символов '[[' идет перевод строки, то он не включается в строковый литерал.
В качестве ограничителей строки кроме двойных квадратных скобок может использоваться символ [===[ … ]===] в котором между повторяющимися квадратными скобками расположено произвольное число знаков равенства (одинаковое для открывающего и закрывающего ограничителя).
В числовых константах можно указывать необязательную дробную часть и необязательный десятичный порядок, задаваемый символами 'e' или 'E'. Целочисленные числовые константы можно задавать в 16-ричной системе, используя префикс 0x.
Комментарий начинается символами '--' (два минуса подряд) и продолжается до конца строки. Если непосредственно после символов '--' идут символы '[[', то комментарий является многострочным и продолжается до символов ']]'. Многострочный комментарий может содержать вложенные пары символов [[….]]. В качестве ограничителей многострочных комментариев кроме двойных квадратных скобок может также использоваться символ [===[ … ]===] в котором между повторяющимися квадратными скобками расположено произвольное число знаков равенства (одинаковое для открывающего и закрывающего ограничителя). Строковая константа экранирует символы начала комментария.
Если первая строка файла начинается с символа '#', то она пропускается. Это позволяет использовать Lua как интерпретатор скриптов в Unix-подобных системах.
Типы данных
В Lua имеются следующие типы данных:
nil | пусто |
boolean | логический |
number | числовой |
string | строковый |
function | функция |
userdata | пользовательские данные |
thread | поток |
table | ассоциативный массив |
Тип nil соответствует отсутствию у переменной значения. Этому типу соответствует единственное значение nil.
Логический тип имеет два значения: true и false.
Значение nil рассматривается как false. Все остальные значения, включая число 0 и пустую строку, рассматриваются как логическое значение true.
Все числа представлены как вещественные числа двойной точности.
Строки представляют собой массивы 8-битных символов и могут содержать внутри себя символ с нулевым кодом. Все строки в Lua константные т.е. изменить содержимое существующей строки нельзя.
Функции можно присваивать переменным, передавать в функции в качестве аргумента, возвращать в качестве результата из функции и сохрнять в таблицах.
Тип userdata соответствует нетипизированному указателю, по которому могут быть расположеы произвольные данные. Программа на Lua не может непосредственно работать с такими данными (создавать, модифицировать их). Этому типу данных не соответствует никаких предопределенных операций кроме присваивания и сравнения на равенство. В то же время такие операции могут быть определены при помощи механизма метаметодов.
Тип thread соответствует независимо выполняемому потоку. Этот тип данных используется механизмом сопрограмм.
Тип table соответствует таблицам — ассоциативным массивам, которые можно индексировать любыми значениями и которые могут одновременно содержать значения произвольных типов.
Тип объекта, сохраненного в переменной можно выяснить, вызвав функцию type(). Эта функция возвращает строку, содержащую каноническое название типа: "nil", "number", "string", "boolean", "table", "function", "thread", "userdata".
Преобразования между числами и строками происходят автоматически в момент их использования в соответствующем контексте.
Арифметические операции подразумевают числовые аргументы и попытка выполнить такую операцию над строками приведет к преобразованию их в числа. Строковые операции, производимые над числами, приводят к их преобразованию в строку с помощью некоторого фиксированного форматного преобразования.
Можно также явно преобразовать объект в строку с помощью функции tostring() или в число с помощью функции tonumber(). Для большего контроля над процессом преобразования чисел в строки следует использовать функцию форматного преобразования.
Переменные
В Lua переменные не нужно описывать. Переменная появляется в момент ее первого использования. Если используется переменная, которая не была предварительно инициализирована, то она имеет значение nil. Переменные не имеют статического типа, тип переменной определяется ее текущим значением.
Переменная считается глобальной, если она явно не объявлена как локальная. Объявление локальных переменных может быть расположено в любом месте блока и может быть совмещено с их инициализацией:
local x, y, z
local a, b, c = 1, 2, 3
local x = x
При инициализации локальной переменной справа от знака равенства вводимая переменная еще не доступна и используется значение переменной, внешней по отношению к текущему блоку. Именно поэтому корректен пример в третьей строке (он демонстрирует часто используемую идиому языка).
Для локальных переменных интерпретатор использует лексические области видимости т.е. область действия переменной простирается от места ее описания (первого использования) и до конца текущего блока. При этом локальная переменная видима в блоках, внутренних по отношению к блоку, в котором она описана. Локальная переменная исчезает при выходе из области видимости. Если локальная переменная определена вне блока, то такая переменная исчезает по окончании исполнения этого участка кода, поскольку участок кода выполняется интерпретатором как безымянная функция. Инициализированная глобальная переменная существует все время функционирования интерпретатора.
Для удаления переменной ей можно просто присвоить значение nil.
Массивы, функции и userdata являются объектами. Все объекты анонимны и не могут быть значением переменной.
Переменные хранят ссылки на объекты. При присваивании, передаче в функцию в качестве аргумента и возвращении из функции в качестве результата не происходит копирование объектов, копируются только ссылки на них.
Таблицы
Таблицы (тип table) соответствует ассоциативным массивам, которые можно индексировать любыми значениями кроме nil и которые могут одновременно содержать значения произвольных типов кроме nil. Элементы таблицы можно индексировать и объектами — таблицами, функциями и объектами типа userdata. Элементы массива, не получившие значения в результате присваивания, имеют по умолчанию значение nil.
Таблицы — основная структура данных в Lua. С их помощью представляются также структуры, классы и объекты. В этом случае используется индексирование строковым именем поля структуры. Поскольку элементом массива может быть функция, в структурах допускаются также и методы.
Для индексирования массивов используются квадратные скобки: array[index]. Запись struct.field эквивалентна следующей записи: struct["field"]. Эта синтаксическая особенность позволяет использовать таблицы в качестве записей с поименованными полями.
Конструктор таблицы — это выражение, создающее и возвращающее новую таблицу. Каждое выполнение конструктора создает новую таблицу. Конструктор таблицы представляет собой заключенный в фигурные скобки список инициализаторов полей (возможно пустой), разделенных запятой или символом ';' (точка с запятой). Для инициализаторов полей допустимы следующие варианты:
[exp1] = exp2 table[exp1] = exp2
name = exp table["name"] = exp
exp table[j] = exp
В последнем случае переменная j пробегает последовательные целые значения, начиная с 1. Инициализаторы первых двух видов не изменяют значение этого счетчика. Вот пример конструирования таблицы:
x = { len = 12, 11, 12, [123] = 1123 }
После выпонения такого оператора поля таблицы получат следующие значения:
x["len"] = x.len = 12
x[1] = 11
x[2] = 12
x[123] = 1123
Если последним элементом списка инициализаторов является вызов функции, то возвращаемые функцией значения последовательно помещаются в список инициализаторов. Это поведение можно изменить, заключив вызов функции в круглые скобки. В этом случае из всех возвращаемых функцией значений используется только первое.
После последнего инициализатора может идти необязательный символ-разделитель инициализаторов полей (запятая или точка с запятой).
Операции
Ниже перечислены основные операции:
- | смена знака |
+ - * / | арифметика |
^ | возведение в степень |
== ~= | равенство |
< <= > >= | порядок |
not and or | логика |
.. | конкатенация строк |
# | получение длины строки или массива |
При применении арифметических операций строки, имеющие числовое значение, приводятся к нему. При конкатенации числовых значений они автоматически преобразуются в строки.
При сравнении на равенство не производится преобразование типов. Объекты разных типов всегда считаются различными.
Соответственно "0" ~= 0, а при индексировании a[0] и a["0"] соответствуют разным ячейкам массива. При сравнении на равенство/неравенство объектов производится сравнение ссылок на объекты. Равными оказываются переменные, ссылающиеся на один и тот же объект.
При выяснении порядка типы аргументов должны совпадать, т.е. числа сравниваются с числами, а строки — со строками.
Отношения равенства и порядка всегда дают в результате true или false т.е. логическое значение.
В логических операциях nil рассматривается как false, а все остальные значения, включая нулевое число и пустую строку — как true.
При вычислении значения используется короткая схема — второй аргумент вычисляется только если это необходимо.
Действует следующая таблица приоритетов и ассоциативности операций:
[right] | ^ |
[left] | not # -(unary) |
[left] | * / |
[left] | + - |
[left] | < > <= >= ~= == |
[right] | .. |
[left] | and |
[left] | or |
Логические операции и связанные с ними идиомы
Оператор not всегда возвращает логическое значение, принимая аргумент произвольного типа (при этом только значение nil соответствует логическому значению false, остальные же трактуются как true). В отличие от него операторы and и or всегда возвращают один из своих аргументов. Оператор or возвращает свой первый аргумент, если его значение отлично от false и nil и второй аргумент в противном случае. Оператор and возвращает свой первый аргумент, если его значение равно false или nil и второй аргумент в противном случае. Такое поведение основано на том, что все значения, отличные от nil, трактуются как true.
С этим поведением связано несколько общеупотребительных идиом. В следующей таблице слева приведена идиоматическая операция, а справа — эквивалентная обычная запись:
x = x or v if x == nil then x = v end
x = (e and a) or b if e ~= nil then x = a else x = b end
Первая идиома часто используется для присвоения неинициализированной переменной умалчиваемого значения. Вторая идиома эквивалентна C'шному оператору x = e ? a, b (здесь считается, что значение переменной a отлично от nil).
Операторы
В Lua нет выделенной функции, с которой начинается выполнение программы. Интерпретатор последовательно выполняет операторы, которые он получает из файла или от управляющей программы. При этом он предварительно компилирует программу в двоичное представление, которое также может быть сохранено. Любой блок кода выполняется как анонимная функция, поэтому в нем можно определять локальные переменные и из него можно возвращать значение.
Операторы можно (но не обязательно) разделять символом ';'.
Допускается множественное присваивание:
var1, var2 = val1, val2
При этом производится выравнивание — лишние значения отбрасываются, а переменным, соответствующим недостающим значениям присваивается значение nil. Все выражения, входящие в правую часть множественного присваивания, вычисляются до самого присвоения.
Если функция возвращает несколько значений, то с помощью множественного присваивания можно получить возвращаемые значения:
x, y, z = f();
a, b, c, d = 5, f();
В общем случае если в конце списка значений, расположенного справа от знака присваивания, находится вызов функции, то все значения, возвращаемые функцией, дописываются в конец списка значений. Это поведение можно изменить, заключив вызов функции в круглые скобки. В этом случае из всех возвращаемых функцией значений используется только первое.
Ниже перечислены основные операторы:
do ... end
if ... then ... end
if ... then ... else ... end
if ... then ... elseif ... then ... end
if ... then ... elseif ... then ... else ... end
while ... do ... end
repeat ... until ...
for var = start, stop do ... end
for var = start, stop, step do ... end
return
return ...
break
Блок do … end превращает последовательность операторов в один оператор и открывает новую область видимости, в которой можно определять локальные переменные.
В операторах if, while и repeat все значения выражения, отличные от false и nil трактуются как истинные.
Вот общая форма записи оператора if:
if ... then ... {elseif ... then ...} [else ...] end
Оператор return может не содержать возвращаемых значений или содержать одно или несколько выражений (список). Поскольку блок кода выполняется как ананимная функция, возвращаемое значение может быть не только у функции, но и у произвольного блока кода.
Операторы return и break должны быть последними операторами в блоке (т.е. должны быть либо последними операторами в блоке кода, либо распологаться непосредственно перед словами end, else, elseif, until). Внутри блока необходимо использовать идиому do return end или do break end.
Оператор for выполняется для всех значений переменной цикла начиная от стартового значения и кончая финишным значением включительно. Третье значение, если оно задано, используется как шаг изменения переменной цикла. Все эти значения должны быть числовыми. Они вычисляются только однажды перед выполнением цикла. Переменная цикла локальна в этом цикле и не доступна вне его тела. Значение переменной цикла нельзя изменять внутри тела цикла. Вот псевдокод, демонстрирующий выполнение оператора for:
do
local var, _limit, _step = tonumber(start), tonumber(stop), tonumber(step)
if not (var and _limit and _step) then error() end
while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do
...
var = var + _step
end
end
Функции
Определение функции — это исполняемое выражение (конструктор функции), результатом вычисления которого является объект типа функция:
f = function( ... ) ... end
В скобках размещается список (возможно пустой) аргументов функции. Список аргументов функции может заканчиваться троеточием — в этом случае функция имеет переменное число аргументов. Между закрывающейся скобкой и оператором end размещается тело функции.
Для определения функции имеются следующие краткие формы:
function fname( ... ) ... end fname = function( ... ) ... end
local function fname( ... ) ... end local fname = function( ... ) ... end
function x.fname( ... ) ... end x.fname = function( ... ) ... end
function x:fname( ... ) ... end x.fname = function( self, ... ) ... end
В момент выполнения конструктора функции строится также замыкание — таблица всех доступных в функции и внешних по отношению к ней локальных переменных. Если функция передается как возвращаемое значение, то она сохраняет доступ ко всем переменным, входящим в ее замыкание. При каждом выполнения конструктора функции строится новое замыкание.
Вызов функции состоит из ссылки на функцию и заключенного в круглые скобки списка аргументов (возможно пустого). Ссылкой на функцию может быть любое выражение, результатом вычисления которого является функция. Между ссылкой на функцию и открывающейся круглой скобкой не может быть перевода строки.
Для вызова функций имеются следующие краткие формы:
f{...} f({...})
f('...') f'...'
f("") f""
f([[...]]) f[[...]]
x:f(...) x.f(x, ...)
В первом случае единственным аргументом является конструируемая на лету таблица, а в следующих трех — строковый литерал. В последнем случае x используется как таблица, из которой извлекается переменная f, являющаяся ссылкой на вызываемую функцию и эта же таблица передается в функцию в качестве первого аргумента. Эта синтаксическая особенность используется для реализации объектов.
При вызове функции значения простых типов копируются в аргументы по значению, а для объектов в аргументы копируются ссылки. При вызове функции производится выравнивание числа аргументов — лишние значения отбрасываются, а аргументы, соответствующие недостающим значениям получают значение nil. Если в конце списка параметров стоит вызов функции, возвращающей несколько значений, то все они добавляются в список аргументов. Это поведение можно изменить, заключив вызов функции в круглые скобки. В этом случае из всех возвращаемых функцией значений используется только первое.
Если функция имеет переменное число аргументов, то значения, не поставленные в соответствие ни одному из аргументов, могут быть получены при использовании специфического имени '…'. Это имя ведет себя как набор значений, возвращаемых функцией т.е. при его появлении внутри выражения или из середины списка значений используется только первое возвращаемое значение. Если же имя '…' оказывается в последней позиции списка значений (в конструкторах таблиц, при множественном присваивании, в списке аргументов при вызове функции и в операторе return), то все возвращаемые значения помещаются в конец этого списка (правило дополнения списков). Для подавления такого поведения имя '…' может быть помещено в круглые скобки. Превратить набор значений в список можно с помощью идиомы {…}.
Если функция принимает единственный аргумент и трактует его как таблицу, элементы которой индексируются именами формальных параметров функции, то в этом случае фактически реализуется вызов механизм поименованных аргументов:
function rename( arg )
arg.new = arg.new or arg.old .. ".bak"
return os.rename(arg.old, arg.new)
end
rename{ old = "asd.qwe" }
Возврат из функции происходит как при завершении выполнения ее тела, так и при выполнении оператора return. Оператор return может возвращать одно или несколько значений. Если в конце списка возвращаемых значений стоит вызов функции, возвращающей несколько значений, то все они добавляются в список аргументов. Это поведение можно изменить, заключив вызов функции в круглые скобки. В этом случае из всех возвращаемых функцией значений используется только первое.
Если функция вызывается как оператор, то все возвращаемые значения уничтожаются. Если функция вызывается из выражения или из середины списка значений, то используется только первое возвращаемое значение. Если же функция вызывается из последней позиции списка значений (в конструкторах таблиц, при множественном присваивании, в списке аргументов при вызове функции и в операторе return), то все возвращаемые значения помещаются в конец этого списка (правило дополнения списков). Для подавления такого поведения вызов функции может быть помещен в круглые скобки.
Имеется предопределенная функция unpack(), которая принимает массив, элементы которого проиндексированы с 1, а возвращает все его элементы. Эта функция может быть использована для вызова функций, принимающих переменное число аргументов с динамическим формированием списка фактических аргументов. Обратная операция производится следующим образом:
list1 = {f()} -- создать список из всех значений, возвращенных функцией f()
list2 = {...} -- создать список из всех значений, переданных в функцию
с переменным числом аргументов
Переменное число возвращаемых значений, правило дополнения списков и возможность передавать в функцию не все формальные параметры могут взаимодействовать нетривиальным образом. Часто функция (например foo()) возвращает ответ при нормальном завершении и nil и сообщение об ошибке при ненормальном. Функция assert(val, msg) генерирует ошибку с сообщением message (вызывая функцию error(msg)) если val имеет значение false или nil и возвращает значение val в противном случае. Тогда оператор
v = assert(foo(), "message")
в случае успеха присваивает переменной v значение, возвращаемое функцией foo(). В этом случае foo() возвращает одно значение, а assert() получает параметр msg, равный nil. В случае ошибки функция assert() получает nil и сообщение об ошибке.
Итераторы
Итераторы используются для перечисления элементов произвольных последовательностей:
for v_1, v_2, ..., v_n in explist do ... end
Число переменных в списке v_1, …, v_n может быть произвольным и не обязано соответствовать числу выражений в списке explist. В роли explist обычно выступает вызов фунции-фабрики итераторов. Такая функция возвращает функцию-итератор, состояние и начальное значение управляющей переменной цикла. Итератор интерпретируется следующим образом:
do
local f, s, v_1 = explist
local v_2, ... , v_n
while true do
v_1, ..., v_n = f(s, v_1)
if v_1 == nil then break end
...
end
end
На каждом шаге значения всех переменных v_k вычисляются путем вызова функции-итератора. Значение управляющей переменной v_1 управляет завершением цикла — цикл завершается как только функция-итератор возвратит nil как значение для переменной var_1.
Фактически итерациями управляет переменная v_1, а остальные переменные можно рассматривать как «хвост», возвращаемый функцией-итератором:
do
local f, s, v = explist
while true do
v = f(s, v)
if v == nil then break end
...
end
end
Оператор, в котором вызывается функция-фабрика, интерпретируется как обычный оператор присваивания т.е. функция-фабрика может возвращать произвольное количество значений.
Итераторы без внутреннего состояния
Итератор без внутреннего состояния не хранит никакой внутренней информации, позволяющей ему определить свое положение в итерируемом контейнере. Следующее значение управляющей переменной вычисляется непосредственно по ее предыдущему значению, а состояние используется для хранения ссылки на итерируемый контейнер. Вот пример простого итератора без внутреннего состояния:
function iter( a, i )
i = i + 1
local v = a[i]
if v then
return i, v
else
return nil
end
end
function ipairs( a )
return iter, a, 0
end
Итераторы, хранящие состояние в замыкании
Если итератору для обхода контейнера необходимо внутреннее состояние, то проще всего хранить его в замыкании, создаваемом по контексту функции-фабрики. Вот простой пример:
function ipairs( a )
local i = 0
local t = a
local function iter()
i = i + 1
local v = t[i]
if v then
return i, v
else
return nil
end
end
return iter
end
Здесь итератор хранит весь контекст в замыкании и не нуждается в состоянии и текущем значении управляющей переменной. Соответственно, итератор не принимает состояние и управляющую переменную, а фабрика не возвращает значение состояния и стартовое значение управляющей переменной.
Стандартные итераторы
Чаще всего итераторы применяются для обхода элементов таблиц. Для этого существует несколько предопределенных функций-фабрик итераторов. Фабрика pairs(t) возвращает итератор, дающий на каждом шаге индекс в таблице и размещенное по этому индексу значение:
for idx, val in pairs(tbl) do
...
end
На самом деле этот итератор легко определить, используя стандартную функцию next(tbl, idx):
function pairs(tbl)
return next, tbl, nil
end
Функция next(tbl, idx) возвращает следующее за idx значение индекса при некотором обходе таблицы tbl (вызов next(tbl, nil) возвращает начальное значение индекса; после исчерпания элементов таблицы возвращается nil).
Фабрика ipairs(tbl) возвращает итератор, работающий совершенно аналогично описанному выше, но предназначенный для обхода таблиц, проиндексированные целыми числами начиная с 1.
Мета-таблицы
Каждая таблица и объект типа userdata могут иметь мета-таблицу — обычную таблицу, поля которой определяют поведение исходного объекта при применении к нему некоторых специальных операций. Например, когда объект оказывается операндом при сложении, интерпретатор ищет в мета-таблице поле с именем __add и, если такое поле присутствует, то использует его значение как функцию, выполняющую сложение. Мета-таблицы позволяют определить поведение объекта при арифметических операциях, сравнениях, конкатенации и индексировании. Также можно определить функцию, вызываемую при освобождении объекта типа userdata. Индексы (имена полей) в мета-таблице называются событиями, а соответствующие значения (обработчики событий) — метаметодами.
По умолчанию вновь созданная таблица не имеет мета-таблицы. Любую таблицу mt можно сделать мета-таблицей таблицы t, вызвав функцию setmetatable(t, mt). Функция getmetatable(t) возвращает мета-таблицу таблицы t или nil, если таблица не имеет мета-таблицы. Любая таблица может выполнять роль мета-таблицы для любой другой таблицы, в том числе и для себя.
Lua определяет следующие события:
__add, __sub, __mul, __div | арифметические операции |
__pow | возведение в степень |
__unm | унарный минус |
__concat | конкатенация |
__eq, __lt, __le | операции сравнения |
__index | доступ по отсутствующему индексу |
__newindex | присвоение новому элементу таблицы |
__call | вызов функции |
__tostring | преобразование в строку |
__metatable | получения мета-таблицы |
Выражение a ~= b вычисляется как not (a == b). Выражение a > b вычисляется как b < a. Выражение a >= b вычисляется как b <= a. При отсутствии метаметода __le операция <= вычисляется как not (b < a) т.е. с помощью метаметода __lt.
Для бинарных операций выбор обработчика производится следующим образом: опрашивается первый операнд и, если он не определяет обработчик, то опрашивается второй операнд. Для операций сравнения метаметод выбирается только если сравниваемые операнды имеют одинаковый тип и одинаковый метаметод для выполнения этой операции. В руководстве пользователя приведен псевдокод на Lua, демонстрирующий контекст вызова метаметодов.
Обработчик события __index может быть функцией или таблицей. В случае функции обработчик вызывается и ему передается таблица и значение индекса. Такая функция должна возвращать результат индексирования. В случае таблицы происходит повторное индексирование этой таблицы тем же индексом. Если присутствует обработчик события __newindex, то он вызывается вместо присвоения значения новому элементу таблицы. Если этот обработчик является таблицей, то присвоение производится в этой таблице.
Метаметод __tostring позволяет обработать преобразование объекта (таблицы или userdata) в строку. Метаметод __metatable позволяет обработать операцию получения мета-таблицы. Если у этого поля в мета-таблице установлено значение, то функция getmetatable() будет возвращать значение этого поля, а функция setmetatable() будет завершаться с ошибкой.
Функция rawget(tbl, idx) позволяет прочитать поле таблицы в обход механизма мета-методов. Аналогичный доступ на запись предоставляет функция rawset(tbl, idx, val). Функция rawequal(t1, t2) сравнивает объекты в обход механизма мета-методов.
Примеры использования мета-таблиц
Значение по умолчанию для полей таблиц
В следующем примере мета-таблицы используются для назначения значения по умолчанию для отсутствующих элементов таблицы:
function set_def(t, v)
local mt = { __index = function() return v end }
setmetatable(t, mt)
end
Вот более нетривиальное решение, которое не использует отдельной мета-таблицы для каждого умалчиваемого значения:
local key = {}
local mt = { __index = function(t) return t[key] end }
function set_def(t, v)
t[key] = v
setmetatable(t, mt)
end
Здесь локальная (пустая) таблица key используется как заведомо уникальный индекс, по которому в исходной таблице t сохраняется умалчиваемое значение d. Все таблицы, для которых устанавливается умалчиваемое значение отсутствующих полей, разделяют общую мета-таблицу mt. Мета-метод __index этой мета-таблицы перехватывает обращения к отсутствующим полям таблицы и возвращает значение, сохраненное в самой таблице по индексу key.
У этого решения есть недостаток: в таблице появляется новая пара ключ-значение, которая проявится при попытке обойти все элементы таблицы.
Таблица-прокси
В следующем примере пустая таблица выполняет роль прокси, переадресующего обращения к полям таблицы:
local key = {}
local mt = {
__index = function(t,k)
return t[key][k]
end,
__newindex = function(t,k,v)
t[key][k] = v
end
}
function proxy(t)
local proxy = {}
proxy[key] = t
setmetatable(proxy, mt)
return proxy
end
Здесь локальная (пустая) таблица key используется как заведомо уникальный индекс, по которому в таблице-прокси сохраняется ссылка на исходную таблицу t. Прокси представляет собой таблицу, единственный элемент которой имеет индекс key, поэтому обращение к любому элементу прокси приведет к вызову мета-метода. Общая для всех прокси мета-таблица определяет мета-методы __index и __newindex, извлекающие исходную таблицу из единственного элемента прокси, индексируя его таблицей key.
Мета-методы могут обеспечить произвольную дисциплину обработки обращений к полям исходной таблицы. Простыми примерами является протоколирование обращений или генерирование ошибки при попытке изменить значение элемента таблицы.
"Слабые" таблицы
Если объект был использован как индекс таблицы или ссылка на него была сохранена в таблице, то сборщик мусора не сможет утилизировать такой объект. В то же время, в некоторых случаях желательно иметь таблицу, в которой связь ее элементов с ключами и/или значениями «слабая» т.е. не препятствующая сбору мусора. Обычно такая необходимость возникает при кэшировании результатов вычислений в таблице и сохранении атрибутов объектов в таблице, проиндексированной самими объектами. В первом случае желательна слабая связь для значений, во втором — для индексов.
Связь элементов таблицы с объектами (значениями и индексами) определяется значением строкового поля __mode ее мета-таблицы. Если это поле содержит символ 'k', то слабой делается связь для индексов (ключей); если оно содержит символ 'v', то слабой делается связь для значений. Поле может содержать оба символа, что сделает слабой связь как для индексов, так и для значений.
Если использовать слабые таблицы, то для рассмотренной выше задачи сопоставления таблице умалчиваемого значения для отсутствующих полей можно привести следующее решение:
local defaults = {}
setmetatable(defaults, { __mode = "k" })
local mt = { __index = function(t) return defaults[t] end }
function set_def(t, d)
defaults[t] = d
setmetatable(t, mt)
end
Вот другое решение, в котором слабая таблицы хранит мета-таблицы, количество которых совпадает с числом различных умалчиваемых значений:
local metas = {}
setmetatable(metas, { __mode = "v" })
function set_def(t, d)
local mt = metas[d]
if mt == nil then
mt = { __index = function () return d end }
metas[d] = mt
end
setmetatable(t, mt)
end
Глобальный контекст
Все глобальные переменные являются полями обычной таблицы, называемой глобальным контекстом. Эта таблица доступна через глобальную переменную _G. Поскольку все глобальные переменные являются полями контекста, то _G._G == _G.
Глобальный контекст делает возможным доступ к глобальным переменным по динамически генерируемому имени:
val = _G[varname]
_G[varname] = val
Поскольку глобальный контекст является обычной таблицей, ему может соответствовать мета-таблица. В следующем примере переменные окружения вводятся в глобальную область видимости как глобальные переменные, доступные только для чтения:
local f = function (t,i)
return os.getenv(i)
end
setmetatable(_G, {__index=f})
Этот же прием позволяет запретить доступ к неинициализированным глобальным переменным.
Пакеты
Пакеты — основной способ определять набор взаимосвязанных функций, не загрязняя при этом глобальную область видимости. Обычно пакет представляет собой отдельный файл, который в глобальной области видимости определяет единственную таблицу, содержащую все функции этого пакета:
my_package = {}
function my_package.foo()
...
end
Также можно делать все функции локальными и отдельно формировать таблицу экспортируемых функций:
local function foo() ... end
local function bar() ... end
my_package = {
foo = foo,
bar = bar,
}
Пакет загружается с помощью функции require(), причем во время загрузки имя, переданное этой функции (оно может не содержать расширения, которое добавляется автоматически) доступно через переменную _REQUIREDNAME:
if _REQUIREDNAME == nil then
run_some_internal_tests()
end
Классы и объекты
Конструкция tbl:func() (при объявлении функции и при ее вызове) предоставляет основные возможности, позволяющие работать с таблицей как с объектом. Основная проблема состоит в порождении многих объектов, имеющих сходное поведение т.е. порожденных от одного класса:
function class()
cl = {}
cl.__index = cl -- cl будет использоваться как мета-таблица
return cl
end
function object( cl, obj )
obj = obj or {} -- возможно уже есть заполненные поля
setmetatable(obj, cl)
return obj
end
Здесь функция class создает пустую таблицу, подготовленную к тому, чтобы стать мета-таблицей объекта. Методы класса делаются полями этой таблицы т.е. класс является таблицей, одновременно содержащей методы объекта и его мета-методы. Функция object() создает объект заданного класса — таблицу, у которой в качестве мета-таблицы установлен заданный класс. Во втором аргументе может быть передана таблица, содержащая проинициализированные поля объекта.
Some_Class = class()
function Some_Class:foo() ... end
function Some_Class:new()
return object(self, { xxx = 12 })
end
x = Some_Class:new()
x:foo()
Наследование
В описанной реализации мета-таблица класса остается не использованной, что делает простой задачу реализации наследования. Класс-наследник создается как объект класса, после чего в нем устанавливающая поле __index таким образом, чтобы его можно было использовать как мета-таблицу:
function subclass( pcl )
cl = pcl:new() -- создаем экземпляр
cl.__index = cl -- и делаем его классом
return cl
end
Теперь в полученный класс-наследник можно добавить новые поля и методы:
Der_Class = subclass(Some_Class)
function Der_Class:new()
local obj = object(self, Some_Class:new())
obj.yyy = 13 -- добавляем новые поля
return obj
end
function Der_Class:bar() ... end -- и новые методы
y = Der_Class:new()
y:foo()
y:bar()
Единственный нетривиальный момент здесь состоит в использовании функции new() из класса-предка с последующей заменой мета-таблицы посредством вызова функции object().
При обращении к методам объекта класса-наследника в первую очередь происходит их поиск в мета-таблице т.е. в самом классе-наследнике. Если метод был унаследован, то этот поиск окажется неудачным и произойдет обращение к мета-таблице класса-наследника т.е. к классу-предку.
Основной недостаток приведенного общего решения состоит в невозможности передачи параметров в функцию new() класса-предка.
Базовая библиотека
Базовая библиотека состоит из функций и переменных, размещенных в глобальном контексте.
Глобальные переменные
- _G [table]
- Содержит глобальный контекст (_G._G = _G). Значение этой переменной не используется интерпретатором и его изменение ни на что не влияет.
- _VERSION [string]
- Содержит номер версии интерпретатора.
- arg [table]
- Массив аргументов, переданных при запуске скрипта. Аргументы занумерованы от 1.
Базовые механизмы
- type(v)
- Возвращает тип аргумента в виде строки. Возможные варианты возвращаемого значения: "nil", "number", "string", "boolean", "table", "function", "thread", "userdata".
- tostring(v)
- Преобразует аргумент любого типа в строку. Если в мета-таблице v есть поле __tostring, то значение этого поля интерпретируется как функция-конвертор и возвращается результат ее вызова с аргументом v.
- tonumber(v [, base])
- Пытается конвертировать аргумент в число. Если конвертирование не удается, то возвращает nil. Необязательный аргумент задает основание системы счисления (от 2 до 36). Для случая base = 10 допускается присутствие дробной части и порядка, для остальных значений число интерпретируется как целое беззнаковое.
Обработка ошибок и отладка
- print(e1, e2, …)
- Печатает значения аргументов на stdout, используя для конвертирования в строку функцию tostring() и разделяя их табуляциями. В конце выводится перевод строки. Эта функция не предназначена для форматного вывода, а только для быстрого (отладочного) вывода значений переменных.
- error(message [, level])
- завершает выполнение последней функции, вызванной в защищенном режиме с сообщением об ошибке message. Необязательный параметр level определяет место возникновения ошибки. Если level = 1 (умалчиваемое значение), то местом ошибки считается место вызова функции error(). Если level = 2, то местом ошибки считается место вызова функции, вызвавшей функцию error() и т.д.
- assert(val [, message])
- Если val имеет значение false или nil, то генерирует ошибку с необязательным сообщением message. В противном случае возвращает значение val. Если message отсутствует, то используется сообщение вида "assertion failed!".
- pcall(f, x1, x2, …)
- Вызывает функцию f с аргументами x1, x2, … в защищенном режиме. Возвращает статус успешности выполнения. В случае успешного выполнения дополнительно возвращает значения, возвращаемые выполненной функцией. При ошибке дополнительно возвращает сообщение об ошибке.
Управление таблицами
- unpack(v)
- Возвращает все элементы массива. Полагает, что элементы проиндексированы числами от 1.
- next(t [, index])
- Аргументы — таблица и значение индекса ее элемента. Возвращает следующее значение индекса и соответствующее значение. При вызове без второго аргумента (или со значением nil) возвращает первое значение индекса и соответствующее значение или nil если таблица пуста. При вызове для последнего значения индекса возвращает nil.
- pairs(t)
- Возвращает итератор next(), таблицу и nil. Возвращаемый итератор проходит таблицу по всем значениям индекса. Итератор возвращает текущий индекс и соответствующее значение.
- ipairs(t)
- Возвращает итератор, таблицу и 0. Возвращаемый итератор проходит таблицу по целочисленным индексам от значения 1 до первого индекса со значением nil. Итератор возвращает текущий индекс и соответствующее значение.
Управление мета-таблицами
- getmetatable(obj)
- Если объект не имеет мета-таблицы, то возвращает nil. Иначе, если в мета-таблице есть поле __metatable, то возвращает соответствующее значение. Иначе возвращает мета-таблицу объекта.
- setmetatable(t, mt)
- Устанавливает таблицу mt в качестве мета-таблицы для таблицы t. Если mt = nil, то удаляет мета-таблицу таблицы t. Если исходная мета-таблица содержит поле __metatable, генерирует ошибку.
Обход механизма метаметодов
- rawequal(v1, v2)
- Сравнивает объекты.
- rawget(t, j)
- Возвращает t[j].
- rawset(t, j, v)
- Выполняет присвоение t[j] = v.
Управление контекстом функций
- getfenv([f])
- Возвращает таблицу контекста функции f. Аргумент может быть числом, определяющим положение функции на стеке вызовов. Значение 1 соответствует функции, вызвавшей функцию getfenv(). Если f = 0, то возвращает глобальный контекст. Умалчиваемое значение f = 1. Если контекст содержит поле __fenv, то возвращается его значение.
- setfenv(f, t)
- Устанавливает таблицу t в качестве контекста для функции f. Аргумент может быть числом, определяющим положение функции на стеке вызовов. Значение 1 соответствует функции, вызвавшей функцию setfenv(). Если f = 0, то изменяет глобальный контекст текущего потока. Если исходный контекст содержит поле __fenv, то генерирует ошибку.
Загрузка и выполнение кода
- loadstring(str [, name])
- Загружает код из строки str и возвращает его как функцию. В случае ошибок при загрузке возвращает nil и сообщение об ошибке. Необязательный параметр name используется как имя кода в сообщениях об ошибках и отладочной информации. Для загрузки и выполнения кода из строки используется идиома assert(loadstring(s))().
- loadfile(filename)
- Загружает код из файла с именем filename и возвращает его как функцию. В случае ошибок при загрузке возвращает nil и сообщение об ошибке. Функция dofile(filename) эквивалентна выражению assert(loadfile(s))().
- dofile(filename)
- Выполняет код из файла с именем filename. Возвращает все значения, возвращенные выполненным кодом.
- package.loadlib(filename, funcname)
- Загружает динамическую библиотеку с именем filename и возвращает lua-функцию, соответствующую функции с именем funcname из этой библиотеки. В случае ошибки загрузки возвращает nil и сообщение об ошибке.
- require(packagename)
- Загружает и выполняет пакет. В первую очередь имя packagename ищется в таблице _LOADED уже загруженных пакетов. Если имя найдено, то возвращается значение, которое пакет возвратил при первой загрузке. В противном случае производится поиск пакета. Если глобальная переменная LUA_PATH содержит строку, то она используется в качестве пути. Иначе в качестве пути используется значение переменной LUA_PATH из окружения. Если такой переменной нет, то используется умалчиваемый путь ("?;?.lua"). Путь поиска представляет собой последовательность шаблонов, разделенных символом ';' (точка с запятой). Имя пакета подставляется в шаблон на место символа '?'. После выполнения пакета его имя и возвращенное значение прописываются в таблицу _LOADED. Возвращенное пакетом значение также возвращается функцией require(). Если пакет возвратил значение false (и только в этом случае), то прописывается и возвращается значение false. Во всех остальных случаях (даже если пакет возвратил значение nil или не возвратил значения) прописывается и возвращается значение true. Если в таблице _LOADED прописалось значение false, то при дальнейших вызовах функции require() пакет будет заново загружен. Если во время загрузки или выполнения произошла ошибка или если файл не найден, то функция генерирует ошибку.
Сборщик мусора
- collectgarbage(opt [, limit])
- Общий интерфейс для управления сборщиком мусора.
Первый аргумент определяет выполняемое действие:
- 'stop'
- Остановить сборщик мусора.
- 'restart'
- Запустить сборщик мусора.
- 'collect'
- Выполнить сборку мусора.
- 'count'
- Вернуть размер используемой памяти в kb.
Стандартные библиотеки
В глобальном контексте библиотеки представлены в виде таблиц, содержащих функции. Доступны следующие библиотеки:
math | математика |
table | таблицы |
string | строки |
io | ввод/вывод |
os | системные вызовы |
debug | средства отладки |
coroutine | сопрограммы |
Математическая библиотека
Математическая библиотека содержит следующие функции:
math.abs | math.acos | math.asin | math.atan | math.atan2 |
math.ceil | math.cos | math.deg | math.exp | math.floor |
math.log | math.log10 | math.max | math.min | math.fmod |
math.pow | math.rad | math.sin | math.sqrt | math.tan |
math.frexp | math.ldexp | math.random | math.randomseed | math.modf |
math.sinh | math.cosh | math.tanh |
Также определяются переменные math.pi и huge, содержащие значения соответствующих констант (M_PI и HUGE_VAL).
Функции math.deg() и math.rad() осуществляют преобразование между радианами и градусами.
Функции math.min() и math.max() возвращают минимальное и максимальное значение для произвольного числа аргументов.
Функция math.modf() возвращает целую и дробную части аргумента. Функция math.frexp() возвращает нормализованную мантиссу и показатель аргумента. Функция math.ldexp(m, e) строит число по мантиссе и показателю.
Функция math.fmod(x, y) возвращает остаток от деления x на y. Функция math.atan2(a, b) возвращает atan(a/b).
Функция math.random(), вызванная без аргументов, возвращает псевдослучайное число из интервала [0, 1). Эта же функция, вызванная с аргументом n возвращает целое псевдослучайное число из интервала [1, n]. Эта же функция, вызванная с двумя аргументами l, u возвращает целое псевдослучайное число из интервала [l, u]. Функция math.randomseed(n) устанавливает стартовое число генератора псевдослучайных чисел. Ее аргумент рассматривается как целое число. При запуске генератор всегда проинициализирован одинаково и выдает одинаковые последовательности. Для случайной стартовой установки можно использовать оператор
math.randomseed(os.time())
Дополнительно математическая библиотека определяет глобальную функцию с именем __pow() для бинарной операции возведения в степень (операция '^').
Управление таблицами
Эта библиотека поддерживает манипулирование таблицами как обычными массивами. В этом случае считается, что массив индексируется, начиная с 1. Длиной массива считается минимальное n такое, что t[n] ~= nil и t[n+1] == nil.
Вставка и удаление
- table.insert(t, [pos, ] val)
- Вставляет элемент val в позицию pos таблицы t, сдвигая остальные элементы таблицы вправо. Умалчиваемое значение параметра pos равно n+1 т.е. по умолчанию происходит вставка в конец таблицы.
- table.remove(t [, pos])
- Удаляет элемент таблицы t в позиции pos, сдвигая остальные элементы таблицы влево. Возвращает значение удаленного элемента. Умалчиваемое значение параметра pos равно длине массива т.е. по умолчанию происходит удаление последнего элемента.
Сортировка
- table.sort(t [, comp])
- Сортирует элементы таблицы в заданном порядке. Если задана функция comp, то она используется для сравнения элементов. Функция comp должна возвращать true, если первый аргумент меньше второго. После сортировки выполнено неравенство not comp(t[j+1], t[j]) Если функция сравнения не задана, то используется стандартный оператор '<'. Алгоритм сортировки не является устойчивым т.е. порядок равных элементов может измениться.
Другие функции
- table.concat(t [, sep [, i [, j]]])
- Возвращает значение t[i] .. sep .. t[i+1] … sep .. t[j]. По умолчанию sep равно пустой строке, i = 1, j = #t. При i > j возвращает пустую строку.
Обработка строк
Позиции символов в строке нумеруются начиная с 1, а отрицательные индексы соответствуют отсчету позиций от конца строки.
Базовые операции
- string.len()
- Возвращает длину строки. Нулевые символы считаются входящими в строку.
- string.rep(s, n)
- Возвращает строку, состоящую из n копий строки s.
- string.lower(s)
- Возвращает копию строки, в которой символы приведены к нижнему регистру.
- string.upper(s)
- Возвращает копию строки, в которой символы приведены к верхнему регистру.
- string.reverse(s)
- Возвращает результат инверсии строки s.
Форматное преобразование
- string.format(fmt, v1, v2, …)
- Генерирует строку по форматной строке и аргументам по правилам, принятым в ANSI-C. Спецификаторы *, l, L, n, p, h не поддерживаются. Дополнительно введен спецификатор 'q', предназначенный для безопасного вывода строки таким образом, чтобы получить ее корректную запись с учетом правил экранирования символов, принятых в Lua. Спецификаторам c, d, E, e, f, g, G, i, o, u, X, x должны соответствовать числовые аргументы, а спецификаторам q, s — строковые. Спецификатор s не умеет выводить строки, содержащие нулевые символы.
Не поддержанный спецификатор '*' можно эмулировать конкатенацией: вместо %*g использовать выражение "%"..width.."g".
На практике имеет смысл использовать следующие спецификаторы
%d, %x, %X, %o | целые числа |
%f, %e, %g | вещественные числа |
%s, %q | строки |
Внутреннее представление
- string.byte(s [, j])
- Возвращает код символа в позиции j. По умолчанию j = 1.
- string.char(n1, n2, …)
- Возвращает строку, состоящую из символов с кодами n1, n2, ….
Поиск и замена
- string.find(s, pat [, start [, plain]])
- Ищет первое вхождение паттерна pat в строке s. Если паттерн найден, то функция возвращает позиции его начала и конца, в противном случае возвращается nil. Если паттерн содержит пометки, то их значения возвращаются как дополнительные значения. Необязательный аргумент start задает позицию, с которой начинается поиск (умалчиваемое значение 1). Если в аргументе plain передать значение true, то в паттерне специальные символы теряют свой специальный смысл.
- string.match(s, pat [, start])
- Ищет первое вхождение паттерна pat в строку s. Если паттерн не содержит пометок, то функция возвращает подстроку, соответствующую паттерну. Если паттерн содержит пометки, то функция возвращает подстроки, соответствующие пометкам. Если паттерн не найден, то функция возвращает nil. Необязательный аргумент start задает позицию, с которой начинается поиск (умалчиваемое значение 1).
- string.gmatch(s, pat)
- Возвращает функцию-итератор, которая на каждой итерации возвращает подстроку, соответствующую паттерну. Если паттерн содержит пометки, то возвращается набор значений пометок.
- string.sub(s, i [, j])
- Возвращает подстроку, начиная с позиции i и до позиции j включительно. Оба индекса могут быть отрицательными. Умалчиваемое значение j = -1.
- string.gsub(s, pat, repl [, n])
- Возвращает копию строки s, в которой все вхождения паттерна pat заменены на строку, заданную аргументом repl. Дополнительно возвращается число сделанных замен. Если repl — строка, то вхождения паттерна заменяются на строку repl, в которой все вхождения последовательности символов вида '%n' (n = 1…9) заменяются на значения группы с соответствующим номером. Если repl — функция, то для каждого вхождения паттерна эта функция вызывается со значениями групп, переданными в качестве аргументов, а возвращаемое значение используется для замены. Если в паттерне нет групп, то в функцию передается вся подстрока, соответствующая паттерну. Если функция возвращает строку, то подставляется эта строка, в противном же случае (false или nil) замена не производится. Необязательный аргумент n задает максимальное число производимых замен.
Паттерны
В Lua паттерн представляет собой обычную строку, в которой некоторые символы интерпретируются специальным образом. В такой строке можно использовать обычные для строк правила экранирования символом '\', поскольку в самих паттернах для экранирования используется символ '%'.
Следующие специальные последовательности определяют классы символов:
'%z' | нулевой символ |
'%s' | пробельный символ |
'%l' | буква в нижнем регистре |
'%u' | буква в верхнем регистре |
'%a' | буква |
'%d' | цифра |
'%x' | шестнадцатеричная цифра |
'%w' | буква или цифра |
'%p' | символ пунктуации |
'%с' | управляющий символ |
Для любого класса символов, представленного последовательностью '%буква' соответствующий класс с той же буквой в верхнем регистре задает дополнительный класс символов.
В паттернах большинство символов имеют обычный смысл, кроме символов %.^$()*-+?[], интерпретируемых специальным образом. Символы, имеющие специальный смысл экранируются символом '%'.
'%' | экранирование или придание специального смысла |
'.' | любой символ |
'^' | начало строки |
'$' | конец строки |
'(…)' | группа |
'%d' | (d — цифра) группа с номером d |
'*' | 0 или больше символов |
'-' | 0 или больше символов (кратчайшее соответствие) |
'+' | 1 или больше символов |
'?' | 0 или 1 символ |
'[…]' | множество символов |
'[^…]' | отрицание множества символов |
%bxy | участок строки между сбалансированными символами |
Во всех случаях для символов из класса '%W' символ '%' означает экранирование символа. Аналогично для символов из класса '%w' символ '%' всегда означает придание символу специального смысла.
Внутри множества символов можно перечислять символы, классы символов (например '%a') и диапазоны символов (например '0-7'). Символ '-' внутри множества приобретает специальный смысл. Аналогично символ '^', следующий непосредственно за символом '[' означает переход к дополнительному множеству.
паттерн '%bxy' служит для выделения участка строки, ограниченного сбалансированными символами 'x' и 'y' .
Участки паттерна могут быть заключены в круглые скобки. Такие участки называются группами. Участок строки, соответствующий группе, называется значением группы. Паттерн '%d' (d — цифра) соответствует значению группы с номером d. Такой паттерн можно использовать как внутри самого регулярного выражения, так и в строке замены. Пустая группа вида '()' получает в качестве значения свою позицию в строке.
Ввод/вывод
Пакет io поддерживает две модели ввода/вывода. В первой модели используются умалчиваемые потоки ввода и вывода, которые можно назначить на заданные файлы. Во второй модели каждому файлу соответствует дескриптор и операции ввода/вывода производятся с этим дескриптором. Также таблица io содержит открытые по умолчанию файловые дескрипторы io.stdin, io.stdout и io.stderr.
Открытие файловых дескрипторов
- io.open(filename [, mode])
- Открывает файл и возвращает файловый дескриптор или nil, сообщение об ошибке и номер ошибки при неудаче.
Допустимые режимы:
'r' | чтение |
'w' | запись |
'a' | добавление |
'r+' | обновление с сохранением |
'w+' | обновление с очисткой |
'a+' | добавление и обновление с сохранением |
Строка режима может содержать на конце букву 'b', что означает открытие файла в бинарном режиме.
- io.popen(prog [, mode])
- Запускает процесс prog и возвращает файл, связанные с потоком ввода (если mode == 'w') или потоком вывода (если mode == 'r', значение по умолчанию) запущенного процесса.
- io.tmpfile()
- Возвращает файловый дескриптор временного файла.
Переназначение умалчиваемых потоков
- io.input([file])
- Устанавливает умалчиваемый поток ввода на переданный дескриптор. Если функция вызвана без аргументов, то она возвращает умалчиваемый дескриптор.
- io.output([file])
- Аналогична io.input(), но устанавливает умалчиваемый поток вывода.
Этим функциям в качестве аргумента можно передавать непосредственно имя файла. В этом случае файл будет открыт в текстовом режиме, но в случае ошибки не происходит возврата кода ошибки.
Форматные строки
При чтении из файла используются следующие форматные строки:
- '*all'
- Чтение всего файла целиком с текущей позиции. При попытке чтения за концом файла и для пустого файла возвращается пустая строка.
- '*line'
- Чтение одной строки (символа конца строки не добавляется в строку). При попытке чтения за концом файла возвращается nil. Это — умалчиваемое поведение для функций чтения.
- '*number'
- Чтение числа. В случае неудачи возвращает nil.
- ddd
- Чтение не более чем заданного числа (ddd) символов. При попытке чтения за концом файла (если не удалось прочитать ни одного символа) возвращается nil. При num = 0 возвращается пустая строка или nil, если достигнут конец файла.
Операции с файлами
Описанные ниже функции присутствуют как в самом пакете io, так и в каждом дескрипторе файла, поэтому здесь они описаны безотносительно контекста вызова. Например, функция read() для умалчиваемого потока ввода вызывается как io.read(), а для файлового дескриптора как file:read().
- read(fmt1, …)
- Читает данные в соответствии со строками формата. Каждой строке формата соответствует одно возвращаемое значение.
- lines()
- Возвращает функцию-итератор по строкам потока ввода.
- write(v1, …)
- Пишет данные в поток вывода. Умеет писать только числа и строки, а для остальных типов данных нужно явно использовать функции tostring() и string.format(). Не добавляет в выходной поток разделители полей и завершители строк.
- flush()
- Сбрасывает буфер потока.
- close()
- Закрывает поток.
В случае умалчиваемых потоков функции flush() и close() относятся к потоку вывода.
Функции io.lines() также можно передать имя файла. В этом случае файл будет открыт в текстовом режиме, а после чтения последней строки файл будет автоматически закрыт. При этом не происходит замены умалчиваемого потока ввода.
Дополнительно файловые дескрипторы имеют функцию seek():
- file:seek([base] [, offset])
- Устанавливает текущую позицию в файле. Смещение offset задает позицию относительно базы, задаваемой параметром base. При успехе возвращает абсолютную позицию в файле, а при неудаче — nil и сообщение об ошибке. Умалчиваемые значения base = "cur", offset = 0.
Допустимые значения параметра base:
"set" | от начала файла |
"cur" | от текущей позиции |
"end" | от конца файла |
Вызов этой функции без параметров возвращает текущую позицию в файле, вызов file:seek("set") позиционирует указатель на начало файла, вызов file:seek("end") позиционирует указатель на конец файла и возвращает его размер.
Системные вызовы
Общие функции
- os.exit([code])
- Завершает выполнение программы, возвращая code как код завершения. По умолчанию возвращает код, соответствующий успешному завершению.
- os.getenv(varname)
- Возвращает значение переменной окружения с именем varname или nil если переменной с таким именем нет.
- os.execute(cmd)
- Выполняет команду cmd и возвращает код завершения.
- os.setlocale(locale [, category])
- Устанавливает локаль. Аргумент locale — это строка, определяющая локаль (у нас — '.1251'). Необязательный аргумент category определяет тип изменяемой локали: "all", "collate" (упорядочение символов), "ctype" (тип символов и регистр), "monetary", "numeric", "time". Умалчиваемая категория — "all". Функция возвращает имя новой локали или nil если установка новой локали не была выполнена.
Управление файлами
- os.remove(filename)
- Удаляет файл с заданным именем. При неудаче возвращает nil и строку с сообщением об ошибке.
- os.rename(old_filename, new_filename)
- Переименовывает файл. При неудаче возвращает nil и строку с сообщением об ошибке.
- os.tmpname()
- Возвращает имя временного файла.
Дата и время
Дата и время представляются в виде таблицы, содержащей поля
year | год (полностью) | |
month | месяц (1-12) | |
day | день (1-31) | |
hour | час (0-23) | * |
min | минута (0-59) | * |
sec | секунда (0-61) | * |
isdst | флаг летнего времени | * |
wday | день недели (Вс = 1, Пн = 2, …) | ** |
yday | номер дня в году | ** |
Звездочкой отмечены поля, необязательные при вызове функции os.time() (используется момент времени 12:00:00). Двумя звездочками отмечены дополнительные поля, возвращаемые функцией os.date().
- os.clock()
- Возвращает время работы программы в секундах.
- os.time([tbl])
- При вызове без аргументов возвращает целое число, представляющее текущее системное время. При вызове с аргументом-таблицей переводит табличное представление даты/времени в целое число, представляющее соответствующее системное время.
- os.difftime(t2, t1)
- Возвращает число секунд, прошедших от момента времени t1 до момента времени t2. Аргументы — целые числа, представляющие системное время.
- os.date([format [, time]])
- Возвращает строковое или табличное представление даты времени, отформатированное в соответствии со строкой format. Если присутствует аргумент time, то форматируется заданное в этом аргументе время. В противном случае форматируется текущее время. Если строка формата содержит '!' в качестве первого символа, то время форматируется в Coordinated Universal Time. Если после этого (необязательного) символа идет строка '*t', то возвращается табличное представление времени. Если строка формата отлична от '*t', то функция возвращает строковое представление времени, интерпретируя строку формата так же, как функция strftime() из ANSI-C. При вызове без аргументов возвращается умалчиваемое строковое представление текущего времени (соответствующее строке формата '%c').
Вот основные спецификаторы для форматирования строкового представления времени:
%% | символ '%' |
%Y | год полностью (1998) |
%y | год кратко (98) [00-99] |
%m | месяц (09) [01-12] |
%d | день (16) [01-31] |
%H | час в 24-часовой шкале (23) [00-23] |
%M | минута (48) [00-59] |
%S | секунда (10) [00-61] |
Другие возможности
Этот документ не претендует на полное описание всех возможностей стандартных библиотек Lua. Ниже перечислены функции, не описанные в этом документе.
Базовая библиотека
- table.maxn(t)
- Возвращает значение максимального положительного числового индекса в таблице t или 0 если таблица не содержит положительных числовых индексов.
- xpcall(func, errfunc)
- Вызов функции func в защищенном режиме. Аналогична pcall(), но не позволяет передать аргументы в функцию func и при ошибках вызывает функцию-обработчик errfunc.
- load(func [, chunkname])
- Аналогична функциям loadfile() и loadstring(), но для получения кода извне вызывает функцию func.
- select(idx, …)
- Если параметр idx имеет числовое значение, то возвращает все аргументы, следующие за аргументом с номером idx. Если idx == '#', то возвращает общее число полученных аргументов.
- module(name [, …])
- Определяет модуль с именем name. Подробное описание см. в описании библиотеки package.
Стандартные библиотеки
В этом документе полностью опущено описание возможностей библиотек package, debug и coroutine.
- io.type(file)
- Возвращает состояние файла file.
- string.dump(func)
- Возвращает двоичное представление функции func.