В lua что значит
Основы декларативного программирования на Lua
Луа (Lua) — мощный, быстрый, лёгкий, расширяемый и встраиваемый скриптовый язык программирования. Луа удобно использовать для написания бизнес-логики приложений.
Отдельные части логики приложения часто бывает удобно описывать в декларативном стиле. Декларативный стиль программирования отличается от более привычного многим императивного тем, что описывается, в первую очередь, каково нечто а не как именно оно создаётся. Написание кода в декларативном стиле часто позволяет скрыть лишние детали реализации.
Луа — мультипарадигменный язык программирования. Одна из сильных сторон Луа — хорошая поддержка декларативного стиля. В этой статье я кратко опишу базовые декларативные средства, предоставлямые языком Луа.
Пример
В качестве наивного примера возьмём код создания диалогового окна с текстовым сообщением и кнопкой в императивном стиле:
function build_message_box ( gui_builder )
local my_dialog = gui_builder:dialog ( )
my_dialog:set_title ( «Message Box» )
local my_label = gui_builder:label ( )
my_label:set_text ( «Hello, world!» )
local my_button = gui_builder:button ( )
В декларативном стиле этот код мог бы выглядеть так:
build_message_box = gui:dialog «Message Box»
Гораздо нагляднее. Но как сделать, чтобы это работало?
Основы
Чтобы разобраться в чём дело, нужно знать о некоторых особенностях языка Луа. Я поверхностно расскажу о самых важных для понимания данной статьи. Более подробную информацию можно получить по ссылкам ниже.
Динамическая типизация
Важно помнить, что Луа — язык с динамической типизацией. Это значит, что тип в языке связан не с переменной, а с её значением. Одна и та же переменная может принимать значения разных типов:
Таблицы
Таблицы (table) — основное средство композиции данных в Луа. Таблица — это и record и array и dictionary и set и object.
Для программирования на Луа очень важно хорошо знать этот тип данных. Я кратко остановлюсь лишь на самых важных для понимания деталях.
Создаются таблицы при помощи «конструктора таблиц» (table constructor) — пары фигурных скобок.
Создадим пустую таблицу t:
Запишем в таблицу t строку «one» по ключу 1 и число 1 по ключу «one»:
Содержимое таблицы можно указать при её создании:
Таблица в Луа может содержать ключи и значения всех типов (кроме nil). Но чаще всего в качестве ключей используются целые положительные числа (array) или строки (record / dictionary). Для работы с этими типами ключей язык предоставляет особые средства. Я остановлюсь только на синтаксисе.
Во-первых: при создании таблицы можно опускать положительные целочисленные ключи для идущих подряд элементов. При этом элементы получают ключи в том же порядке, в каком они указаны в конструкторе таблицы. Первый неявный ключ — всегда единица. Явно указанные ключи при выдаче неявных игнорируются.
Следующие две формы записи эквивалентны:
Во-вторых: При использовании строковых литералов в качестве ключей можно опускать кавычки и квадратные скобки, если литерал удовлетворяет ограничениям, налагаемым на луашные идентификаторы.
При создании таблицы следующие две формы записи эквивалентны:
Аналогично для индексации при записи…
Функции
Функции в Луа — значения первого класса. Это значит, что функцию можно использовать во всех случаях, что и, например, строку: присваивать переменной, хранить в таблице в качестве ключа или значения, передавать в качестве аргумента или возвращаемого значения другой функции.
Функции в Луа можно создавать динамически в любом месте кода. При этом внутри функции доступны не только её аргументы и глобальные переменные, но и локальные переменные из внешних областей видимости. Функции в Луа, на самом деле, это замыкания (closures).
function make_multiplier ( coeff )
return function ( value )
return value * coeff
local x5 = make_multiplier ( 5 )
Важно помнить, что «объявление функции» в Луа — на самом деле синтаксический сахар, скрывающий создание значения типа «функция» и присвоение его переменной.
Следующие два способа создания функции эквивалентны. Создаётся новая функция и присваивается глобальной переменной mul.
function mul ( lhs, rhs ) return lhs * rhs end
mul = function ( lhs, rhs ) return lhs * rhs end
Вызов функции без круглых скобок
В Луа можно не ставить круглые скобки при вызове функции с единственным аргументом, если этот аргумент — строковый литерал или конструктор таблицы. Это очень удобно при написании кода в декларативном стиле.
print ( «Shopping list:» )
for name, qty in pairs ( items ) do
Цепочки вызовов
Как я уже упоминал, функция в Луа может вернуть другую функцию (или даже саму себя). Возвращённую функцию можно вызвать сразу же:
chain_print ( 1 ) ( «alpha» ) ( 2 ) ( «beta» ) ( 3 ) ( «gamma» )
В примере выше можно опустить скобки вокруг строковых литералов:
chain_print ( 1 ) «alpha» ( 2 ) «beta» ( 3 ) «gamma»
Для наглядности приведу эквивалентный код без «выкрутасов»:
local tmp1 = chain_print ( 1 )
local tmp2 = tmp1 ( «alpha» )
local tmp3 = tmp2 ( 2 )
local tmp4 = tmp3 ( «beta» )
local tmp5 = tmp4 ( 3 )
Методы
Объекты в Луа — чаще всего реализуются при помощи таблиц.
За методами, обычно, скрываются значения-функции, получаемые индексированием таблицы по строковому ключу-идентификатору.
Луа предоставляет специальный синтаксический сахар для объявления и вызова методов — двоеточие. Двоеточие скрывает первый аргумент метода — self, сам объект.
Следующие три формы записи эквивалентны. Создаётся глобальная переменная myobj, в которую записывается таблица-объект с единственным методом foo.
function myobj:foo ( b )
function myobj.foo ( self, b )
myobj [ «foo» ] = function ( self, b )
print ( self [ «a_» ] + b )
Примечание: Как можно заметить, при вызове метода без использования двоеточия, myobj упоминается два раза. Следующие два примера, очевидно, не эквивалентны в случае, когда get_myobj() выполняется с побочными эффектами.
Чтобы код был эквивалентен варианту с двоеточием, нужна временная переменная:
local tmp = get_myobj ( )
При вызове методов через двоеточие также можно опускать круглые скобки, если методу передаётся единственный явный аргумент — строковый литерал или конструктор таблицы:
Реализация
Теперь мы знаем почти всё, что нужно для того, чтобы наш декларативный код заработал. Напомню как он выглядит:
build_message_box = gui:dialog «Message Box»
Что же там написано?
Приведу эквивалентную реализацию без декларативных «выкрутасов»:
local tmp_1 = gui : label ( «Hello, world!» )
local tmp_2 = gui : button ( «OK» )
local button = tmp_2 ( < >)
local tmp_3 = gui : dialog ( «Message Box» )
Интерфейс объекта gui
Как мы видим, всю работу выполняет объект gui — «конструктор» нашей функции build_message_box(). Теперь уже видны очертания его интерфейса.
Опишем их в псевдокоде:
Декларативный метод
В интерфейсе объекта gui чётко виден шаблон — метод, принимающий часть аргументов и возвращающий функцию, принимающую остальные аргументы и возвращающую окончательный результат.
Для простоты, будем считать, что мы надстраиваем декларативную модель поверх существующего API gui_builder, упомянутого в императивном примере в начале статьи. Напомню код примера:
function build_message_box ( gui_builder )
local my_dialog = gui_builder:dialog ( )
my_dialog:set_title ( «Message Box» )
local my_label = gui_builder:label ( )
my_label:set_text ( «Hello, world!» )
local my_button = gui_builder:button ( )
Попробуем представить себе, как мог бы выглядеть метод gui:dialog():
return function ( element_list )
return function ( gui_builder )
local my_dialog = gui_builder:dialog ( )
element_list [ i ] ( gui_builder )
Ситуация с [gui_element] прояснилась. Это — функция-конструктор, создающая соответствующий элемент диалога.
Функция build_message_box() создаёт диалог, вызывает функции-конструкторы для дочерних элементов, после чего добавляет эти элементы к диалогу. Функции-конструкторы для элементов диалога явно очень похожи по устройству на build_message_box(). Генерирующие их методы объекта gui тоже будут похожи.
Напрашивается как минимум такое обобщение:
function declarative_method ( method )
return function ( self, name )
return function ( data )
return method ( self, name, data )
Теперь gui:dialog() можно записать нагляднее:
gui.dialog = declarative_method ( function ( self, title, element_list )
return function ( gui_builder )
local my_dialog = gui_builder:dialog ( )
element_list [ i ] ( gui_builder )
Реализация методов gui:label() и gui:button() стала очевидна:
gui.label = declarative_method ( function ( self, text, parameters )
return function ( gui_builder )
local my_label = gui_builder:label ( )
if parameters.font_size then
gui.button = declarative_method ( function ( self, title, parameters )
return function ( gui_builder )
local my_button = gui_builder:button ( )
— Так сложилось, что у нашей кнопки нет параметров.
Что же у нас получилось?
Проблема улучшения читаемости нашего наивного императивного примера успешно решена.
В результате нашей работы мы, фактически, реализовали с помощью Луа собственный предметно-ориентированный декларативный язык описания «игрушечного» пользовательского интерфейса (DSL).
Благодаря особенностям Луа реализация получилась дешёвой и достаточно гибкой и мощной.
В реальной жизни всё, конечно, несколько сложнее. В зависимости от решаемой задачи нашему механизму могут потребоваться достаточно серьёзные доработки.
Например, если на нашем микро-языке будут писать пользователи, нам понадобится поместить выполняемый код в песочницу. Также, нужно будет серьёзно поработать над понятностью сообщений об ошибках.
Описанный механизм — не панацея, и применять его нужно с умом как и любой другой. Но, тем не менее, даже в таком простейшем виде, декларативный код может сильно повысить читаемость программы и облегчить жизнь программистам.
Полностью работающий пример можно посмотреть здесь.
Хочу всё знать. Язык Lua
Краткая справка
Lua бы придуман в 1993 году в Католическом университете Рио-де-Жанейро. Название переводится с португальского, как Луна, причем создатели убедительно просят не писать LUA, чтобы, не дай Бог, кто-нибудь не принял название за аббревиатуру. Является мультипарадигмальным скриптовым языком, использующим прототипную модель ООП.
По внешнему виду, да и возможностям Lua похож на очередную попытку переделать JavaScript, если бы не тот факт, что последний появился на два года позднее. Смотрите сами:
Начнем с традиционного:
function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end
Все предельно понятно. Кстати, в Lua поддерживается параллельное присваивание:
И в заключении довольно простой пример с использованием библиотек:
#include
#include
#include
#include
#include
int main (void) <
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */
Преимущества и недостатки
Итак, чем же хорош Lua?
Во-первых, как уже было отмечено, своей компактностью, а вкупе с тем, что исходники написаны на С, вы получаете полное взаимодействие с одним из популярнейших языков на планете и широкий спектр доступных платформ.
Во-вторых, он быстрый. Если взглянуть на сравнительную характеристику с другими языками, то можно заметить, что хоть Lua и не укладывает на лопатки C или Python, но в некоторых тестах показывает отличные результаты.
В-третьих, он очень удобен для изучения даже не самыми опытными программистами. Наверняка даже те, кто просто знает английский язык, поняли минимум 80% описанного выше кода и без проблем смогут воспроизвести.
Любителей современных тенденций привлечет тот факт, что на Lua можно писать функциональный код.Да что там, если вы хотя бы пару недель уделите программированию на Lua, то наверняка найдёте ещё не один десяток плюсов.
Впрочем, не существует языков без недостатков, но у Lua они носят локальный характер. Так, например, язык по умолчанию не поддерживает Unicode, но это исправляется с использованием специальной ICU библиотеки. Или ограниченные возможности обработки ошибок и исключений, хотя многие сочтут это за благо. Или необходимость ставить оператор return исключительно последним в блоке, но опять-таки для многих это естественное правило хорошего кода.
Хотите стать веб-разработчиком? Тогда вам на наш курс обучения web-разработке!
Среды разработки
Полезные ссылки
Наш сегодняшний гость — настоящий боец скрытого фронта. Вы могли видеть его в играх (World of Warcraft, Angry Birds, X-Plane, S.T.A.L.K.E.R.) или продуктах компании Adobe (Lightroom), но даже не задумывались о его существовании. Между тем этому языку уже почти 25 лет и всё это время он незаметно делал нашу виртуальную жизнь чуть лучше.
Краткая справка
Lua бы придуман в 1993 году в Католическом университете Рио-де-Жанейро. Название переводится с португальского, как Луна, причем создатели убедительно просят не писать LUA, чтобы, не дай Бог, кто-нибудь не принял название за аббревиатуру. Является мультипарадигмальным скриптовым языком, использующим прототипную модель ООП.
По внешнему виду, да и возможностям Lua похож на очередную попытку переделать JavaScript, если бы не тот факт, что последний появился на два года позднее. Смотрите сами:
Начнем с традиционного:
function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end
Все предельно понятно. Кстати, в Lua поддерживается параллельное присваивание:
И в заключении довольно простой пример с использованием библиотек:
#include
#include
#include
#include
#include
int main (void) <
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */
Преимущества и недостатки
Итак, чем же хорош Lua?
Во-первых, как уже было отмечено, своей компактностью, а вкупе с тем, что исходники написаны на С, вы получаете полное взаимодействие с одним из популярнейших языков на планете и широкий спектр доступных платформ.
Во-вторых, он быстрый. Если взглянуть на сравнительную характеристику с другими языками, то можно заметить, что хоть Lua и не укладывает на лопатки C или Python, но в некоторых тестах показывает отличные результаты.
В-третьих, он очень удобен для изучения даже не самыми опытными программистами. Наверняка даже те, кто просто знает английский язык, поняли минимум 80% описанного выше кода и без проблем смогут воспроизвести.
Любителей современных тенденций привлечет тот факт, что на Lua можно писать функциональный код.Да что там, если вы хотя бы пару недель уделите программированию на Lua, то наверняка найдёте ещё не один десяток плюсов.
Впрочем, не существует языков без недостатков, но у Lua они носят локальный характер. Так, например, язык по умолчанию не поддерживает Unicode, но это исправляется с использованием специальной ICU библиотеки. Или ограниченные возможности обработки ошибок и исключений, хотя многие сочтут это за благо. Или необходимость ставить оператор return исключительно последним в блоке, но опять-таки для многих это естественное правило хорошего кода.
Хотите стать веб-разработчиком? Тогда вам на наш курс обучения web-разработке!
Среды разработки
3 – Описание языка
Примечание: В информатике лексический анализ — процесс аналитического разбора входной последовательности символов (например, такой как исходный код на одном из языков программирования) с целью получения на выходе последовательности символов, называемых «токенами» (подобно группировке букв в слова). Группа символов входной последовательности, идентифицируемая на выходе процесса как токен, называется лексемой. В процессе лексического анализа производится распознавание и выделение лексем из входной последовательности символов.
Языковые конструкции будут разъясняться при помощи обычной расширенной BNF нотации, в которой <a> означает 0 или больше символов a, а выражение [a] означает необязательный символ a. Нетерминалы показаны как non-terminal, ключевые слова (keywords) показаны как kword, другие терминальные символы показаны как ‘=’. Полный синтаксис Lua можно найти в §9 в конце этого руководства.
3.1 – Лексические соглашения
Имена (также называемые идентификаторами) в Lua могут быть любой строкой из букв, цифр и символов подчеркивания, но не должны начинаться с цифры. Идентификаторы используются для именования переменных, полей таблиц и меток.
Следующие строки обозначают другие токены: literal strings могут быть заключены соответственно, в одиночные или двойные кавычки, и могут содержать следующие C-подобные escape-последовательности:
Обратный слеш (backslash) с последующим реальным символом новой строки в результате приведут к переносу части строки на новую строку. Escape-последовательность ‘\z‘ пропускает последующий диапазон разделительных (white-space) символов, включая переносы (break) строки; это особенно полезно для прерывания и отступа длинной буквенной (литеральной) строки в многострочном тексте без добавления символов новой строки и пробела в содержимое строки.
Буквенные строки (литералы) также могут быть определены использованием долгого формата заключением в длинные скобки. Мы определяем открывающую длинную скобку уровня вложенности n как открывающую квадратную скобку с последующим n-ным числом знаков равенства, сопровождаемые другой открывающей квадратной скобкой. Так, открывающая длинная скобка уровня 0 записывается как [[, а открывающая длинная скобка уровня 1 записывается как [=[, и так далее. Аналогично определяется и закрывающая длинная скобка; например, закрывающая длинная скобка уровня 4 записывается как ]====]. Длинный литерал начинается с открывающей длинной скобки любого уровня и заканчивается первой закрывающей длинной скобкой того же уровня. Она может содержать любой текст, кроме закрывающей скобки того же уровня. Литералы в этой «скобочной» форме могут продолжаться до нескольких строк, в них не выполняется интерпретация любых управляющих последовательностей и игнорируются длинные скобки любого другого уровня. Любой тип управляющей последовательности окончания строки (возврат каретки, новая строка, возврат каретки, сопровождаемый новой строкой, или новая строка с последующим возвратом каретки) преобразуется в простой символ новой строки.
Любой байт в литеральной строке, явно не затронутые предыдущими правилами, представляет самого себя. Однако, Lua открывает файлы для анализа (парсинга) в текстовом режиме и у функций системных файлов могут возникать проблемы с некоторыми управляющими символами. Так что более безопаснее представлять не текстовые данные в виде закавыченных литералов с явно указанными управляющими последовательностями для нетекстовых символов.
3.2 – Переменные
Одиночное (отдельное) имя может обозначать глобальную переменную или локальную переменную (или формальный (т.е. соответствующий правилам) параметр функции, который является особым видом локальной переменной): Name обозначает идентификаторы, как было определено в §3.1.
Любая переменная name предполагается глобальной, если явно не объявлена как локальная (смотрите §3.3.7). Локальные переменные ограничены лексически: локальные переменные могут быть свободно доступны функциям, определенным внутри их области видимости (смотрите §3.5).
Перед первым присваиванием переменной, её значением является nil.
Квадратные скобки используются для обозначения индексов таблицы: Значение обращений к полям таблицы может быть изменено с помощью метатаблиц. Обращение к индексированной переменной t[i] аналогично вызову gettable_event(t,i) (смотрите §2.4 с полным описанием функции gettable_event. Эта функция не определена и не подлежит вызову в Lua. Здесь она приведена только в разъяснительных целях.)
3.3 – Операторы
3.3.1 – Блоки
3.3.2 – Порции
Порция может быть сохранена в файл или строку внутри хост-программы. Для выполнения порции (chunk), Lua сначала загружает её, предварительно компилируя код порции в инструкции для виртуальной машины, и затем Lua выполняет скомпилированный код с интерпретатором виртуальной машины.
Порции также можно предварительно скомпилировать в двоичную форму; для более подробных сведений смотрите программу luac и функцию string.dump. Программы в исходной и компилированной формах взаимозаменяемы; Lua автоматически распознает тип файла и действует соответственно (смотрите load).
т.е. — пооператорный (покомандный, построчный) анализ, обработка и тут же выполнение исходной программы или запроса (в отличие от компиляции, при которой программа транслируется без её выполнения).
Простой интерпретатор анализирует и тут же выполняет (собственно интерпретация) программу покомандно (или построчно), по мере поступления её исходного кода на вход интерпретатора. Достоинством такого подхода является мгновенная реакция. Недостаток — такой интерпретатор обнаруживает ошибки в тексте программы только при попытке выполнения команды (или строки) с ошибкой.
Интерпретатор компилирующего типа — это система из компилятора, переводящего исходный код программы в промежуточное представление, например, в байт-код или p-код, и собственно интерпретатора, который выполняет полученный промежуточный код (так называемая виртуальная машина). Достоинством таких систем является большее быстродействие выполнения программ (за счёт выноса анализа исходного кода в отдельный, разовый проход, и минимизации этого анализа в интерпретаторе). Недостатки — большее требование к ресурсам и требование на корректность исходного кода.
3.3.3 – Присваивание
Lua разрешает множественные присваивания. Поэтому, синтаксис присваивания определяет список переменных с левой стороны, а список выражений с правой стороны. Элементы в обоих списках разделяются запятыми: Выражения обсуждаются в §3.4.
Перед присваиванием список значений согласовывается с длиной списка переменных. Если значений больше чем нужно, избыточные значения отбрасываются. Если значений меньше чем требуется, в список добавляются значения nil до нужного количества. Если список выражений оканчивается вызовом функции, то все значения возвращаемые этим вызовом вводятся в список значений, перед согласованием (кроме случаев, когда вызов заключен в круглые скобки; смотрите §3.4).
Оператор присваивания сначала вычисляет все свои выражения и только затем присваивание выполняется. Таким образом, код устанавливает a[3] равным 20, не затрагивая a[4], так как i в a[i] вычисляется (как 3) перед присвоением ему 4. Аналогично, строка обменивает значения x и y, а циклически переставляет значения x, y, и z.
Значение присваиваний глобальным переменным и полям таблиц может быть изменено с помощью метатаблиц. Присваивание индексированной переменной t[i] = val эквивалентно settable_event(t,i,val). (смотрите в §2.4 полное описание функции settable_event. Эта функция в Lua не определяется и не вызывается, она используется здесь только в разъяснительных целях.)
Присваивание глобальному имени x = val эквивалентно присваиванию _ENV.x = val (смотрите §2.2).
3.3.4 – Управляющие структуры
Управляющие структуры if, while, и repeat имеют обычное значение и знакомый синтаксис:
В Lua также имеется оператор цикла for, в двух вариантах (смотрите §3.3.5).
Условное выражение управляющей структуры может возвращать любое значение. Значения false и nil считаются ложными. Все значения, отличные от nil и false считаются истинными (в частности, число 0 и пустая строка также являются истинными).
В цикле repeat–until, внутренний блок завершается не при достижении ключевого слова until, а только после выполнения условия. Так что, условие может ссылаться на локальные переменные, объявленные внутри блока цикла.
Оператор goto передает управление программой на метку (label). По синтаксическим причинам, метки в Lua также считаются операторами: Метка является видимой во всем блоке, где она объявлена, за исключением мест внутри вложенных блоков, где определена метка с таким же именем, и внутри вложенных функций. goto может перейти на любую видимую метку до тех пор, пока не входит в область видимости локальной переменной.
Метки и пустые операторы называются недействующими операторами (void statements), так как они не выполняют действий.
Оператор break завершает выполнение while, repeat, или цикла for, переходя к следующему оператору после цикла: break завершает самый внутренний вложенный цикл.
Оператор return используется для возврата значений из функции или порции (которая является анонимной функцией). Функции могут возвращать более одного значения, так что синтаксис для оператора return таков Оператор return может быть записан только в качестве последнего оператора блока. Если действительно необходимо, чтобы return был в середине блока, то можно использовать явно заданный внутренний блок, в виде идиомы do return end, так как теперь return является последним оператором в своем (внутреннем) блоке.
3.3.5 – Оператор for
Оператор цикла for имеет две формы: числовую и универсальную (типовую).
3.3.6 – Вызовы функций как операторы
3.3.7 – Локальные объявления
Локальные переменный могут быть объявлены в любом месте внутри блока. Объявление может включать начальное присваивание: Если таковое существует, начальное присваивание имеет ту же семантику как и множественное присваивание (смотрите §3.3.3). В противном случае, все переменные инициализируются со значением nil.
Порция также является блоком (смотрите §3.3.2) и поэтому локальные переменные могут быть объявлены в порции за пределами любого явного блока.
Правила видимости для локальных переменных поясняются в §3.5.
3.4 – Выражения
Основные выражения в Lua следующие: Числа и символьные строки (литеральные строки) описаны в §3.1; переменные в §3.2; определения функций описаны в §3.4.11; вызовы функций в §3.4.10; конструкторы таблиц описываются в §3.4.9. Выражения с переменным числом аргументов (vararg), обозначаемые тремя точками (‘. ‘), могут использоваться только непосредственно внутри функций с переменным числом аргументов (vararg function); они поясняются в §3.4.11.
В бинарные операторы включены арифметические операторы (смотрите §3.4.1), побитовые операторы (смотрите §3.4.2), операторы сравнения (смотрите §3.4.4), логические операторы (смотрите §3.4.5), и оператор конкатенации (смотрите §3.4.6). В унарные операторы включены унарный минус (смотрите §3.4.1), унарный побитовый НЕ (смотрите §3.4.2), унарный логический not (смотрите §3.4.5), и унарный оператор длины (смотрите §3.4.7).
И вызовы функций, и выражения с переменным числом аргументов (vararg expressions) могут приводить к множеству значений. Если вызов функции используется как оператор (смотрите §3.3.6), то его список возврата устанавливается в ноль элементов, таким образом отбрасываются все возвращаемые значения. Если выражение используется в качестве последнего (или единственного) элемента списка выражений, то такая корректировка не делается (если выражение не заключено в круглые скобки). Во всех других ситуациях, Lua сводит список результатов к одному элементу, либо отбрасывая все значения за исключением первого из них, либо добавляя одиночный nil, если значений не существует.
Вот некоторые примеры: Любые выражения заключенные в круглые скобки всегда выдают только одно значение. Таким образом, (f(x,y,z)) это всегда одно значение, даже если f возвращает несколько значений. (Значением из (f(x,y,z)) является первым значением, возвращенным f или значение nil, если f не возвратит никаких значений.)
3.4.1 – Арифметические операторы
Возведение в степень и деление чисел с плавающей запятой (/) всегда конвертирует свои операнды в числа с плавающей запятой и всегда получает результат в виде числа с плавающей запятой. Возведение в степень использует ISO C функцию pow, так что он также работает и с нецелочисленными показателями степени.
Модуль определяется как остаток от деления, при котором частное округляется в направлении к минус бесконечности. (деление с округлением вниз).
В случае переполнений (когда для представления получившегося числа не хватает разрядности) в целочисленной арифметике, все операции циклически переносятся (wrap around), в соответствии с обычными правилами арифметики дополнительных кодов. (Другими словами, они возвращают уникальное представляемое целое число, равное остатку от деления 2 64 на результат математического вычисления.)
3.4.2 – Побитовые операторы
Lua также конвертирует строки в числа всякий раз, когда предполагается число.
В преобразовании целого числа в число с плавающей запятой, если целочисленное значение имеет точное представление как число с плавающей запятой, то оно и будет результатом преобразования. В другом случае, при преобразовании получается ближайшее большее или ближайшее меньшее число от представленного значения. Преобразования такого рода никогда не делают ошибок.
При преобразовании числа с плавающей запятой в целое число проверяется, не имеет ли число с плавающей запятой точного представления в виде целого числа (то есть проверяется, что число с плавающей запятой имеет целочисленное значение и оно находится в диапазоне целочисленных представлений). Если это так, то это представление и является результатом. В противном случае преобразование завершается неудачей.
Преобразование строк в числа происходит следующим образом: Вначале, строка конвертируется в целое число или число с плавающей запятой, следуя своему синтаксису и правилам лексического анализатора Lua. (Строка также может иметь начальные или конечные пробелы и знак.) Затем, полученное число (целое или с плавающей запятой) преобразуется в тип (целое число или число с плавающей запятой), требуемый по контексту (например, операцией, которая вызвала преобразование).
Преобразование чисел в строки использует не указанный удобочитаемый формат. Для полного управления тем, как числа преобразуются в строки, используйте функцию format из строковой библиотеки (смотрите string.format).
3.4.4 – Операторы сравнения
Равенство (==) вначале сравнивает тип своих операндов. Если типы различны, то в результате выдается false. В противном случае сравниваются значения операндов. Строки сравниваются банальным способом. Числа равны, если они означают одно и тоже математическое значение.
Таблицы, userdata и нити (потоки) сравниваются по ссылке: два объекта считаются равными, только если они являются одним и тем же объектом. Каждый раз при создании нового объекта (таблица, userdata или нить), этот новый объект отличается от любого, уже существующего объекта. Замыкания с одной и той же ссылкой всегда равны. Замыкания с любой обнаруженной разницей (разное поведение, разное определение) всегда различны.
Изменить способ, которым Lua сравнивает таблицы и userdata, можно при помощи метаметода «eq» (смотрите §2.4).
При сравнениях на равенство не производится конвертирование строк в числа и наоборот. Таким образом, "0"==0 определяется как false, а t[0] и t["0"] обозначают разные записи в таблице.
= является точным отрицанием равенства (==).
Операторы порядка работают следующим образом. Если оба аргумента являются числами, то они сравниваются в соответствии с их математическими значениями (не обращая внимания на их подтипы). Иначе, если оба аргумента являются строками, то их значения сравниваются в соответствии с текущей локалью. В противном случае, Lua пытается вызвать метаметод «lt» или «le»смотрите §2.4). Сравнение a > b преобразуется в b = b преобразуется в b указывает на результат предыдущего выражения.)
3.4.6 – Конкатенация
3.4.7 – Оператор длины
Оператор длины обозначается одиночным оператором-приставкой #. Длиной строки является количество байт в ней (то есть, обычное значение длины строки, когда каждый из символов занимает один байт).
Программа может изменять поведение оператора длины для любого значения, а для строк через метаметод __len (смотрите §2.4).
Если не задан метаметод __len, длина таблицы t определяется только если таблица является последовательностью, то есть, набор её положительных цифровых ключей равен для некоторого неотрицательного целого числа n. В этом случае, n является её длиной. Обратите внимание, что таблица вроде не является последовательностью, так как она имеет ключ 4, но не имеет ключа 3. (Так как не существует такого n, что набор равен набору положительных цифровых ключей этой таблицы.) Отметьте впрочем, что нечисловые ключи не мешают таблице быть последовательностью.
3.4.8 – Приоритет
реализуемая когда операции имеют одинаковый приоритет и отсутствует явное (с помощью скобок) указание на очерёдность их выполнения. Ассоциативность (от лат. associatio) — свойство операций, позволяющее восстановить последовательность их выполнения при отсутствии явных указаний на очерёдность при равном приоритете; при этом различается левая ассоциативность, при которой вычисление выражения происходит слева направо, и правая ассоциативность — справа налево. Соответствующие операторы называют левоассоциативными и правоассоциативными.
3.4.9 – Конструкторы таблиц
Если последнее поле в списке имеет форму exp и это выражение является вызовом функции или выражением с переменным числом аргументов (vararg выражением), то все значения, возвращенные этим выражением, последовательно вводятся в список (смотрите §3.4.10).
Поле списка может иметь необязательный завершающий разделитель, для удобства при машинной генерации кода.
3.4.10 – Вызовы функций
Вызов функции в Lua имеет следующий синтаксис: или на русском языке В вызове функции, сначала вычисляются префиксные выражения (prefixexp) и аргументы (args). Если значение prefixexp имеет тип function, то эта функция вызывается с заданными аргументами. В противном случае, prefixexp вызывает метаметод «call», имеющий в качестве первого параметра значение prefixexp, с последующим вызовом исходных аргументов (смотрите §2.4).
Форма записи может использоваться для вызова «методов». Вызов в виде v:name(args) это «синтаксический сахар» для v.name(v,args), за исключением того, что v вычисляется только один раз.
Вызов в виде return functioncall называется хвостовым вызовом (tail call). В Lua реализованы правильные хвостовые вызовы (или правильная хвостовая рекурсия): в хвостовом вызове, вызываемая функция повторяет запись стека вызывающей функции. Поэтому не существует ограничения на число вложенных хвостовых вызовов, которые может выполнить программа. Однако, хвостовой вызов стирает любую отладочную информацию о вызывающей функции. Обратите внимание, что хвостовой вызов происходит только при конкретном синтаксисе, где return в качестве аргумента имеет один, единственный вызов функции; такой синтаксис делает возврат вызывающей функции в точности равным возврату вызываемой функции. Так что, ни один из следующих примеров не является хвостовым вызовом:
3.4.11 – Определения функций
Синтаксис определения функции Следующий «синтаксический сахар» упрощает определения функций: Заявление преобразуется в Заявление преобразуется в Заявление преобразуется в а не в (Различие появляется только когда тело функции содержит ссылки на f.)
Параметры действуют как локальные переменные, которые инициализированы значениями аргументов: При вызове функции список аргументов корректируется по длине со списком параметров, если функция не является функцией с переменным числом аргументов (vararg функция), которая обозначается тремя точками (‘. ‘) в конце своего списка параметров. Функция с переменным числом аргументов не корректирует список своих аргументов; вместо этого, она собирает все дополнительные аргументы и передает их в функцию через выражение с переменным числом аргументов (vararg выражение), которое также обозначается как три точки. Значением этого выражения является список всех текущих дополнительных аргументов, похожий на функцию с множественным результатом. Если выражение с переменным числом аргументов используется внутри другого выражения или в середине списка выражений, то его список возврата сводится к одному элементу. Если выражение используется как последний элемент в списке выражений, то такая корректировка не производится (если это последнее выражение не заключено в круглые скобки).
В качестве примера рассмотрим следующие определения: Далее получаем следующее сопоставление от аргументов к параметрам и vararg выражению: Результаты возвращены при помощи оператора return (смотрите §3.3.4). Если управление доходит до конца функции не встречая оператор return, то функция ничего не возвращает.
Существует системно-зависимый предел на число значений, которые может возвращать функция. Этот предел гарантированно больше 1000.
Синтаксис с двоеточием используется для определения методов, то есть, функций, имеющих неявный дополнительный параметр self. Таким образом, выражение это «синтаксический сахар» для
3.5 – Правила видимости
Lua является языком программирования с лексическим разграничением. Область видимости локальной переменной начинается с первого оператора после её объявления и продолжается до последнего не пустого оператора самого внутреннего блока, который включает объявление. Рассмотрим следующий пример: Обратите внимание, что в объявлении local x = x, новая переменная x будучи объявлена, ещё не находится в области видимости, и поэтому второй x относится к внешней переменной.
Из-за правил лексического разграничения, локальные переменные могут быть широко доступны функциям, определенным в их области видимости. Локальная переменная используемая внутренней функцией называется upvalue, или внешней локальной переменной, внутри вложенной функции.
Заметьте, что каждое выполнение оператора local определяет новую локальную переменную. Рассмотрим следующий пример: Цикл создает десять замыканий (closure) (то есть, десять экземпляров анонимной функции). Каждый из этих замыканий использует разную переменную y, хотя все они используют одну и ту же переменную x.
в окружающем коде и не являющиеся её параметрами. Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своём контексте.
Замыкание, так же как и экземпляр объекта, есть способ представления функциональности и данных, связанных и упакованных вместе.
Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.
В случае замыкания ссылки на переменные внешней функции действительны внутри вложенной функции до тех пор, пока работает вложенная функция, даже если внешняя функция закончила работу, и переменные вышли из области видимости.
Замыкание связывает код функции с её лексическим окружением (местом, в котором она определена в коде). Лексические переменные замыкания отличаются от глобальных переменных тем, что они не занимают глобальное пространство имён. От переменных в объектах они отличаются тем, что привязаны к функциям, а не объектам.


