lua pairs что это
Lua pairs что это
В этой части будет описано несколько важных инструкций языка Lua. Под инструкцией (statement) понимается некоторая самостоятельная часть программы, которую обычно пишут с новой строки. Например, из первой статьи читатель уже поверхностно знаком с двумя видами инструкций языка — присваиванием (=) и вызовом функции (на примере функции print).
Lua поддерживает следующие инструкции для управления потоком выполнения (иначе говоря, для изменения порядка выполнения инструкций):
В целом, они повторяют аналогичные конструкции из других языков. Особенной является только, пожалуй, generic for, но она во всей своей красе будет рассмотрена позже.
if-else
Инструкция if реализует ветвление, то есть позволяет выполнить тот или иной блок кода в зависимости от истинности некоторого условия.
В качестве условия может выступать любое выражение. В качестве блока — любой набор инструкций. Всё как обычно: если условие истинно, то блок выполняется. Если нет, то нет (опять я переборщаю с подробностями, да?). Здесь, как и везде в Lua, применяются те же правила определения истинности выражения: любое значение, кроме false и nil, является истинным.
Заметьте, что присваивание в Lua не является выражением, как в Си-образных языках. И это правильно, ибо, говорят, новички иногда путают = и ==.
Конечно, можно дополнить if блоком else, который выполняется только в случае, если условие ложно:
Если условий требуется несколько, можно воспользоваться elseif. Это аналогично ещё одному вложенному if в блоке else, но пишется короче. В результате выполняется только один из блоков. Пример из PiL:
Инструкции switch из Си здесь нет. Скорее всего, авторы языка посчитали её избыточной, так как она слишком редко требуется и при необходимости может быть сымитирована средствами более общего назначения, например, тем же if-elseif-else.
while
Инструкция while повторно выполняет блок кода до тех пор, пока истинно условие. Проверка условия производится перед очередным выполнением блока. Вот так, например, можно вывести числа Фибоначчи, меньшие 100:
repeat-until
repeat-until повторно выполняет блок кода до тех пор, пока условие не станет истинным. То есть, until — это по сути “while not”. Ещё одно отличие от while — проверка условия производится после выполнения блока, так что блок выполняется в любом случае хотя бы один раз. Этим инструкция похожа на do-while из Си.
Логичнее всего эту инструкцию использовать в случае, если мы “стремимся” к выполнению условия: ждём правильного ответа, ищем нужный элемент и др.
numeric for
Числовой цикл for позволяет шагать по числовым значениям из указанного диапазона. При этом в каждую итерацию указанная переменная-счётчик принимает соответствующее числовое значение.
Каждое из трёх выражений (от, до, шаг) должно принимать числовое значение. Выражения вычисляются только один раз, перед выполнением цикла. Обратите внимание, что как нижняя, так и верхняя границы включаются в диапазон.
Ни границы, ни шаг не обязаны быть целыми. Дробные числа будут работать так, как ожидается.
Шаг можно не указывать, тогда он принимается равным 1. Какой ещё есть популярный пример использования циклов, помимо чисел Фибоначчи? Конечно же, вычисление факториала!
Шаг может быть отрицательным, чтобы считать в обратную сторону, но тогда нужно указывать соответствующие “от” и “до”, чтобы “от” было больше, иначе ни одной итерации не выполнится.
Часто, если переменная-счётчик внутри цикла не используется, её называют _ (подчёркивание), чтобы это было сразу видно.
Числовой цикл можно использовать для инициализации или обхода элементов массивов:
Для инициализации вложенных (многомерных) массивов можно использовать вложенные числовые циклы:
generic for
generic (обобщённый) for — довольно мощная конструкция, которая позволяет совершать обход значений, генерируемых специально сформированной функцией. Более подробно generic for будет рассмотрен позже, а пока стоит ознакомиться с его использованием для обхода элементов таблицы. Для этого существуют стандартные функции ipairs и pairs.
ipairs
Функция ipairs используется для обхода “массивной” части таблицы.
Такой цикл перебирает все элементы с целочисленными ключами в порядке возрастания, начиная с единицы, до тех пор, пока не наткнётся на значение nil. При этом в две переменные, указываемые после слова “for”, попадают номер элемента и его значение. Вторую переменную, кстати, можно не указывать, если она не нужна.
В отличие от конструкции с использованием числового for и оператора #, цикл с ipairs гарантированно останавливается перед первым nil. Поведение же оператора # на массивах с дырами не определено, как это было сказано в предыдущей части. Таким образом, на “недырявых” массивах разница будет проявляться разве что в производительности — цикл ipairs работает чуть медленнее (хотя и в большинстве случаев некритично). Зато выглядит он всё-таки покрасивее.
pairs
Функция pairs используется для обхода вообще всех элементов таблицы.
Заметьте, что порядок обхода элементов таблицы в этом случае непредсказуем (даже для целочисленных ключей), и полагаться на него нельзя. Если всё же требуется иметь возможность обращаться к значениям по ключам и при этом обходить их в определённом порядке, то можно хранить эти ключи отдельно в виде массива в ещё одной таблице.
При обходе таблицы с использованием pairs можно без опаски удалять элементы из таблицы, присваивая им значение nil, и это не приведёт к тому, что какая-либо из существующих в таблице пар будет пропущена при обходе. Однако, добавление новых элементов в таблицу во время её обхода не рекомендуется, так как в этом случае поведение не определено.
Кстати, если вы замечаете, что вам часто требуется определять наличие элемента в массиве, каждый раз пробегая циклом ipairs по всем элементам, или даже иногда удалять эти элементы из массива, то стоит подумать, действительно ли вам нужно хранить эти элементы в определённом порядке. Если вам не важен порядок, и элементы являются гарантированно уникальными, то гораздо эффективнее будет хранить их как ключи в таблице, используя в качестве значений любое истинное значение, как в примере выше.
Любой цикл (while, repeat-until, for) можно принудительно прервать, используя инструкцию break:
А вот инструкции continue в языке нет. Придётся либо переорганизовывать код, чтобы в ней отпала необходимость, либо… использовать goto!
Инструкция goto позволяет перепрыгнуть на другое место в коде. Для этого нужно указать в этом месте именованную метку.
Метка видна только в пределах блока, в котором она определена (и во вложенных в него блоках), то есть, нельзя прыгнуть в блок, который не включает в себя текущую инструкцию, с которой мы собрались прыгать.
Инструкция goto позволяет реализовать не только обычное continue, но даже вложенное continue или break, т.е. при использовании вложенных циклов прервать выполнение внешнего.
В принципе, реальная необходимость в этой инструкции возникает нечасто. Я бы посоветовал ей не увлекаться, чтобы не превратить код в спагетти.
Lua. Краткое введение в метатаблицы для чайников
На написание данной статьи меня сподвигло большое количество вопросов по метатаблицам и ООП в Lua, благо это самый сложный и проблематичный раздел у изучающих данный язык, но, так как Lua проектировалась как язык для начинающих и не-программистов и, в целом, имеет небольшой объём материала для освоения, негоже оставлять её «на потом», учитывая что с помощью метатаблиц можно творить чудеса и крайне элегантные решения заковыристых задач.
В данной публикации будет описание всех стандартных мета-методов для версий Lua 5.1-5.3, с примерами. Начнём.
Метатаблицы
На самом деле, метатаблица ничем не отличается от обычной таблицы, за исключением того что она указана как управляющая.
Схематично можно представить, например, так:
В метатаблице описывается реакция основной таблицы на воздействия, например, вызов таблицы как функцию, деление таблицы на произвольное значение, или попытка вытянуть из неё ключ, которого у неё нет, благодаря специальным ключам (Lua 5.1, если не указано обратное):
Общие метаметоды
Математические метаметоды и сравнение (функции)
Примеры
Index
Один из самых распространённых метаметодов, и вызывающий наибольшее число вопросов.
Может быть таблицей или функцией, с аргументами (self, key), где self — таблица для поиска, а key — ключ, значение которого мы хотим получить.
На основе этого метаметода строится большое количество фич, таких как ООП, прокси-таблицы, дефолтные значения таблиц, и ещё много чего.
Иногда может быть вреден, когда необходимо получить точный ключ ВОТ ЭТОЙ таблицы, в таких случаях используют функцию value = rawget(table, key), которая является функцией доступа у таблиц по умолчанию (она, в байткоде, вызывается при попытке получения значения по ключу).
Что тут происходит:
1. foo — таблица, в которой мы будем искать ключи, которых у нас нет.
2. mt — таблица, с ключом __index = foo. Если её прицепить к чему-то как метатаблицу, она будет указывать: «Если у нет ключей — попробуйте найти их в foo».
3. Тут — процесс цепляния метатаблицы mt к пустой таблице (которой становится bar)
4. Пример прямого доступа к ключам таблицы. В данном случае, мы берём ключ как обычно, из таблицы bar.
5. Пример доступа к ключам по __index. В данном случае, в таблице bar отсутствует ключ [«key»], и мы ищем его по __index метатаблицы — в таблице foo.
6. Уточнение: если мы внесём в таблицу bar ключ key, он найдётся в ней и при попытке забрать значение — не будет вызвана цепочка метаметодов. Но все остальные несуществующие ключи, такие как [«foo»], будут продолжать вызывать цепочку метаметодов. Ключ [«foobarsnafu»] отсутствует в обеих таблицах, и его значение — закономерный nil.
7. Index позволяет создавать цепочки поиска. В данном случае, алгоритм поиска ключа следующий:
1. Ищем ключ [«key»] в таблице snafu.
2. Не нашли. У метатаблицы snafu есть ключ __index, указывающий на таблицу bar. Ищем там.
3. Опять не нашли. Но и там есть метатаблица с ключом __index, указывающей на таблицу foo. Ищем.
4. Нашли! Вот он наш ключ, и его значение — «FooBar»!
8. В данном случае — мы создаём таблицу с ключом __index, равном ей самой, и устанавливаем её как метатаблицу для самой себя. При попытке получения значения по любому отсутствующему ключу, возникает рекурсивный цикл попыток поиска внутри себя самой, и переходов по __index метатаблицы и дальнейшего поиска. Поэтому, лучше не делать замкнутые цепочки поиска. Если использовать rawget — ни один метаметод не вызывается, и мы получаем точное значение данного ключа.
9. В качестве ключа __index у метатаблицы может быть функция с двумя аргументами — self — сама таблица, key — ключ, значение которого мы хотим получить. Возвращаемое значение функции становится значением. С помощью этого, можно создавать произвольную индексацию у таблиц, или создавать прокси.
10. Пример взят с википедии. В данном случае, __index у таблицы fibs автоматически пересчитывает значения чисел фибоначчи с мемоизацией, т.е. print(fibs[10]) выведет десятое число фибоначчи. Работает через рекурсивное вычисление отсутствующих значений таблицы. Последующие значения мемоизируются в таблицу. Нужно немного времени чтобы понять: если fibs[v — 1] отсутствует — для него выполняется тот же набор действий что и для fibs[v].
NewIndex
Не настолько распространённый метаметод, но тоже иногда удобный для создания закрытых таблиц, фильтрации или проксирования, и ещё нескольких вещей.
Всегда может быть только функцией, с аргументами (self, key, value).
Иногда может быть вреден, поэтому для принудительного не-использования данного метаметода используется функция rawset(self, key, value), который является функцией для таблиц по умолчанию.
1. Это — простейший пример добавления ключей через прокс-таблицу с помощью метаметода __newindex. Все новые ключи-значения, которые мы добавляем в таблицу bar, добавляются в foo в соответствии с функцией. Self, в данном случае — таблица bar;
2. __newindex распространяется только на несуществующие ключи;
3. Пример функции-фильтра, которая позволяет добавлять в таблицу только числовые значения. Точно так же можно проверять «добавляем только числовые ключи», или заранее создаём несколько таблиц для чисел-строк-таблиц и т.п, и добавляем значения в соответствующие (классификация/балансировка и т.п.).
Данный метаметод удобен для сокращения элементов или вызова дефолтных методов функций с таблицами и для чуть более комфортного ООП, когда мы вызываем таблицу-класс как функцию, и получаем объект.
1. Пример использования метатаблицы, таблицу можно вызывать как функцию. В качестве self передаётся сама таблица, вызванная как функция.
2. В данном примере, мы заполняем таблицу функциями, а метатаблица указывает, что если её вызовут как функцию — выдать результат фунции под ключом default.
Tostring и Concat
Просто приведение объекта к строке и конкатенация.
Metatable
Скрытие метатаблиц, иногда бывает полезно.
Строка, указывает режим связей значений таблиц. Если она содержит букву ‘k’, то слабыми будут объявлены ключи, если содержит букву ‘v’ — слабыми станут значения. Можно использовать их вместе. В примерах будет использоваться функция collectgarbage — принудительный сбор всего мусора.
Таблицы в Lua — всегда передаются по ссылке.
1. Пример таблицы слабых значений: если нет ссылок на значения, кроме как в этой таблице — они удалятся в процессе сборки мусора.
2. После исполнения данного участка кода, на таблицу
«
«
3. Мы видим, что пока в таблице foo есть все значения, но после сборки мусора — исчезает таблица key2. Это происходит потому, что на неё не осталось больше сильных ссылок, только слабые, которые позволяют мусорщику её собрать. К foo[1] это не относится, так как 100500 — не ссылочный тип (не таблица, не функция, не userdata и т.п, а число).
4. Если мы удалим единственную сильную ссылку bar — таблица
Аналогичным образом работает с ‘k’, только по отношению к ссылочным ключам таблицы (foo[
Функция __gc вызовется в том случае, если таблица будет собрана сборщиком мусора. Может использоваться как финалайзер. Функционирует с таблицами и cdata/userdata.
Функция, переопределяющая алгоритм вычисления длины таблицы (Lua 5.2+).
Pairs и Ipairs
Переопределение стандартных итераторов таблиц, для данной таблицы (Lua 5.2+).
Перегрузка операторов
Перегрузка всех операторов работает по одной схеме, детальные примеры для каждого — не нужны.
1. Пример перегрузки операторов на таблицах, которые ведут себя как векторы, благодаря метатаблице. Следует следить за порядком аргументов, каждая операция — возвращает новую таблицу-«вектор».
2. Таблица, в которую можно добавлять элементы оператором «+».
Порядок добавления определяет, в конец или в начало мы добавляем элемент.
Что со всем этим можно сделать
Первое что напрашивается — попытка сделать ООП.
Попробуем написать простую функцию, реализующую некоторый абстрактный «класс»:
Вот, уже что-то похожее на ООП. Тут нет наследования и всяких крутых штук, но это уже неплохо.
При вызове rect.area, у таблицы-объекта нет ключа area, поэтому она идёт искать его через __index у таблицы-класса, находит, и подставляет туда саму себя первым аргументом.
Малое отступление от метатаблиц: пример второго вызова — первое появление в данной статье двоеточия. Двоеточие — синтаксический сахар языка Lua. Если вызвать функцию в таблице через двоеточие а не точку, первым аргументом в эту функцию подставится сама таблица, поэтому тот код эквивалентен.
Можно попробовать слегка улучшить данный вариант.
Во-первых, добавить возможность вызывать класс как функцию с возвратом объекта, во-вторых, добавить возможность перегрузки операторов у самого класса, В третьих — наследование.
Таким образом, в 15 строк полезного кода, можно имплементировать в Lua максимум из действительно необходимого ООП.
Конечно, тут есть что улучшать и чем обвешивать, и подобная работа проведена в библиотеках middleclass или hump.Class, но иногда полезно и такое.
Кстати, если не хочется заморачиваться с функциями-классами, а нужно просто написать один-два класса, можно пользоваться конструкцией отсюда.
Прокси-таблицы
Вот тут, наконец-то, пример полноценной прокси, с отслеживанием действий.
На выходе таблица, которая логирует её использование. В данном случае, таблица-прокси всегда пуста, в ней нет никаких ключей, поэтому __newindex будет вызываться каждый раз.
Таблицы временных объектов
Время от времени, могут понадобиться временные объекты, которые существуют некоторое время, но при нехватке памяти — освобождают занимаемое пространство. Данный пример потребует наличия библиотеки Luasec (https-запросы), хотя с тем же успехом можно использовать Luasocket, только без https.
Пока всё
Я считаю, что данного материала достаточно чтобы более-менее освоить метатаблицы, если есть интересные или забавные примеры — пишите в комментариях.
Для желающих задать кучу вопросов — оставляю ссылку на чатик в тележке.
Русские Блоги
Lua серии-пары и пары
Стол для хранения в Lua
Прежде чем смотреть на разницу между ними, давайте сначала посмотрим, как таблицы в Lua распределяются в памяти.
Состав таблицы:
1. Хеш-таблица используется для хранения пар ключ-значение ключ-значение. При возникновении конфликта в хеш-таблице элементы конфликта будут организованы по связанному списку.
2. Массив используется для хранения данных (включая числа, таблицы и т. д.)
Давайте рассмотрим простой пример.
Видеть выходные результаты немного необъяснимо, и обход нормального контакта кажется другим.
Давайте посмотрим, какие изменения в памяти произошли в средней таблице.
Как данные хранятся в таблице t:
1) В зависимости от типа элемента сохраните его в хеш-таблице и массиве:
Хеш-таблица: <[1] = 1, [3] = 3, [5] = 5, [6] = 6>;
Массив: <2, 4>
2) Поместите элементы в массиве в хеш-таблицу:
Когда элементы в массиве переносятся в хеш-таблицу, элементами в массиве становятся [1] = 2 и [2] = 4, и уже есть ключи в хеш-таблице Пара значений [1] = 1, возникает конфликт, и новое значение 2 будет снова сопоставляться с ключом 1, то есть новая пара значений ключа [1] = 2, и элементы хеш-таблицы в это время:
Проверка отладки точки останова,
Выше чисто цифровой контент, давайте попробуем взглянуть на смешанный контент цифрового и табличного
Таблицы в Lua в конечном итоге размещаются в памяти в виде пар ключ-значение
Или в соответствии с первым хешем (пара ключ-значение), затем массив (значение) для выделения,
Таблица хранится в порядке при сохранении значений, но она сохраняется в соответствии с хэш-значением ключа при сохранении пар ключ-значение,
не сохраняется в алфавитном или числовом порядке.
Обратите внимание на последнюю таблицу, даже если элемент пустой, ноль, ему будет присвоен ключ
Если вы хотите проверить, вы можете проверить это самостоятельно.
пары и пары
Разница между двумя обходами,
pairs
Функции обхода индекса
1. Порядок обхода случайный, но он будет проходить по всей таблице.
2. Пары сначала печатаются в соответствии со значением индекса (числовой ключ, для доступа к которому вы можете использовать []), а затем печатаете хеш (пара ключ-значение)
когда прекратить
1. Все элементы пройдены
Очень подробно разжёвано для чайников по LUA часть2!
Расширенная форма оператора for
В расширенной форме оператора for для последовательного получения значений переменной цикла используется вызов итератора. Цикл завершается, когда итератор возвращает nil.
Под итератором понимается любая конструкция, позволяющая перебирать элементы некоторого набора. При каждом обращении к итератору он возвращает очередной элемент набора. В Lua итераторы обычно реализуются в виде функций.
Расширенная форма оператора for имеет следующий вид:
for var1, var2, …, varN in do
— список выражений, разделённых запятыми. Обычно список состоит из единственного выражения — вызова функции-фабрики итераторов. Такая функция возвращает функцию-итератор, состояние и начальное значение управляющей переменной цикла.
Оператор for в расширенной форме имеет те же особенности, что и числовой for:
значения переменных цикла нельзя изменять внутри цикла.
В качестве примера использования расширенной формы оператора for рассмотрим типичный код, выполняющий обход всех полей таблицы.
for key, val in pairs(t) do
MsgBox («key == »..key..»; val == «..val)
Список переменных в данном примере включает два элемента — key и val. На каждом шаге цикла переменная keyполучает ключ очередного поля таблицы t, а переменная val — соответствующее ключу значение поля. В список выражений входит только один элемент — вызов функции-фабрики итераторов pairs.
Работает расширенный оператор for следующим образом:
Вызывает функцию pairs(t), от которой принимает три значения:
стандартную функцию next в качестве итератора;
таблицу, которую требуется обойти (t), в качестве состояния;
nil в качестве начального значения управляющей переменной цикла.
Вызов функции pairs выполняется только один раз.
Оператор for приступает к выполнению, собственно, итераций цикла:
вызывает функцию-итератор next с двумя параметрами: таблицей t и nil. Функция next, вызванная с этими параметрами, возвращает начальный ключ таблицы и соответствующее ему значение (при условии, что таблица не пуста);
вновь вызывает функцию next, передавая ей таблицу t и ключ, полученный на первой итерации. Функция nextвозвращает следующую пару ключ-значение. Этот процесс продолжается до тех пор, пока функция next не вернётnil.
Описываемые действия можно представить в виде следующего кода:
local f, s, next_key = pairs(t)
local key, val = f(s, next_key)
if next_key == nil then break end
MsgBox(«key == »..key..»; val == «..val)
Выполняющий все описанные действия цикл for можно реализовать и без использования функции pairs. Известно, чтоpairs возвращает функцию next и таблицу, которую следует обойти. Поэтому вызов pairs можно заменить списком соответствующих переменных:
for key, val in next, t do
MsgBox («key == »..key..»; val == «..val)
Помимо pairs, стандартные библиотеки Lua предоставляют ещё несколько функций-фабрик итераторов. Так, для перебора элементов массива предусмотрена функция ipairs, а для итерирования по строкам файла — функция io.lines.
Вы можете создавать свои собственные итераторы и фабрики итераторов. Для получения необходимой информации рекомендуем Вам обратиться к специализированной литературе по программированию на языке Lua.
Операторы break и return в Lua
Оператор break прерывает цикл (while, repeat или for), в теле которого встречается. В результате выполнения оператора break управление передаётся первой инструкции, следующей непосредственно за оператором цикла.
for i = 1,#a do — ищем в массиве отрицательное значение
do return end — Правильно
Создание таблиц в Lua. Работа с полями
Создать пустую таблицу можно следующим образом:
Для доступа к полю таблицы используется запись вида:
t = <> — создаем пустую таблицу
t[1] = «first» — новое поле таблицы, с ключом 1 и значением «first»
t[2] = 20 — новое поле, с ключом 2 и значением 20
t[k] = «Jane» — новое поле, с ключом «name» и значением «Jane»
a = t[1] — переменная a получает значение «first»
b = t[2] — переменная b получает значение 20
c = t[«name»] — переменная c получает значение «name»
В случае строковых ключей вместо записи t[«name»] можно использовать запись t.name:
t.name = «name» — эквивалентно t[«name»] = «name»
a = t.name — эквивалентно a = t[«name»]
Выражение t.name не равнозначно t[name]. Первое выражение представляет поле таблицы, ключом которого являетсястрока «name» (то есть эквивалентно t[«name»]). Второе выражение представляет поле, ключом которого является значение переменной name. Различия между этими выражениями показаны в следующем примере:
t[name] = «Jane» — в поле «somebody» помещено значение «Jane»
a = t[name] — переменная a получает значение поля «somebody» («Jane»)
b = t.name — поля «name» не существует, переменная b получает nil
c = t.somebody — переменная c получает значение поля «somebody» («Jane»)
Если поля таблицы с заданным ключом не существует, обращение к нему дает nil:
a = t.name — переменная a получает значение nil
Для удаления поля таблицы достаточно присвоить ему nil:
Таблицу можно заполнить значениями непосредственно при создании. Для этого в фигурных скобках следует перечислить ключи и значения элементов таблицы (в формате [ключ]=значение). Элементы отделяются друг от друга запятыми (,) или точками с запятой (;):
Приведённая запись эквивалентна следующему коду:
t.red = «красный»; t.green = «зеленый»; t.blue = «синий»
В случае строковых ключей квадратные скобки (и двойные кавычки) можно не указывать:
Если необходимо создать таблицу, поля которой также являются таблицами, это можно сделать следующим образом:
Приведённая запись эквивалентна следующему коду:
Работа с массивами в Lua
Массив — это таблица, ключами которой являются целые положительные числа. Чтобы создать массив, достаточно перечислить в фигурных скобках значения его элементов:
Это выражение эквивалентно следующему коду:
В Lua массивы индексируются, начиная с 1 (а не с 0, как в некоторых языках программирования).
Оператор получения длины #, применённый к массиву, возвращает его максимальный индекс (или размер):
В примере ниже приведён ряд типичных для Lua синтаксических конструкций (идиом), основанных на использовании оператора #:
a = t[#t] — присвоим переменной a значение последнего элемента массива t
t[#t] = nil — удалим последний элемент массива t
t[#t+1] = a — добавим значение переменной a в конец массива t
При работе с массивами следует учитывать важную особенность оператора #. Этот оператор рассматривает любой неинициализированный (имеющий значение nil) элемент массива как признак конца массива. Поэтому, например, для таблицы:
t = <[1]=«first», [3]=«third»>— элемент с индексом 2 отсутствует (t[2]==nil)
оператор получения длины вернёт 1, а не 2 или 3. Таким образом, для корректной работы оператора # необходимо, чтобы массив не содержал «пустых» элементов.
Если таблица не содержит целочисленных ключей (либо элемент с индексом 1 равен nil), оператор # возвращает 0:
n = #t — n равно 0 (поскольку t[1] имеет значение nil)
Обход элементов таблицы в Lua
Для обхода всех элементов таблицы обычно используют расширенную форму оператора for совместно со стандартной функцией pairs.
for key, val in pairs(t) do
На каждом шаге цикла переменная key получает ключ очередного поля таблицы t, а переменная val — соответствующее ключу значение поля. Цикл выполняется по всем полям таблицы.
Для обхода массива, то есть таблицы с целочисленными ключами, удобнее использовать другую стандартную функцию Lua — ipairs.
for i, val in ipairs(t) do
На каждом шаге цикла переменная i получает числовой индекс очередного поля таблицы t, а переменная val— соответствующее индексу значение поля. Цикл продолжается до первого целого ключа, отсутствующего в таблице.
При обходе массива необходимость в получении индекса зачастую отсутствует. В этом случае переменную i можно не вводить, заменив её символом подчеркивания (_).
for _, val in ipairs(t) do
Обойти массив можно и с помощью обычного числового for.
MsgBox(«Значение элемента №»..i..»: «..t[i])
Определение функций в Lua
Типичное определение функции выглядит следующим образом:
return x*y — тело функции
Как видно из этого примера, определение функции состоит из следующих элементов:
ключевого слова function;
имени (f в данном примере);
заключённого в круглые скобки списка аргументов функции (возможно, пустого);
тела функции, размещённого между закрывающей скобкой и ключевым словом end.
Приведённое выше определение функции является просто более удобным способом записи следующего выражения:
return x*y — тело функции
Таким образом, определение функции фактически выполняет два действия:
Создаёт объект типа «функция» (это делает выражение function (x,y)… end).
Помещает ссылку на созданную функцию в переменную или поле таблицы (в нашем примере — в переменную f).
Имя f принадлежит переменной, в которую помещена ссылка на функцию, но не самой функции. Любые функции в Lua анонимны, то есть не имеют имён. Когда говорят об имени функции, например, «функция f», на самом деле подразумевают переменную f, содержащую ссылку на эту функцию. У переменных, содержащих ссылки на функции, нет жёсткой привязки к самим функциям; работа с такими переменными осуществляется точно так же, как и с любыми другими переменными.
function f(x,y) return x*y end — создаем функцию, переменная f содержит ссылку на эту функцию
s = f — переменная s ссылается на ту же функцию, что и f
t.sqr = f — поле sqr таблицы t ссылается на ту же функцию, что и f
f = MsgBox — f теперь «превратилась» в функцию MsgBox
Несмотря на то, что обычно ссылка на созданную функцию присваивается переменной, такое присваивание не является обязательным. В приведённом ниже примере создаётся безымянная функция, которая сразу передаётся в качестве параметра в функцию table.sort:
table.sort (t, function (a, b) return (a > b) end)
Вызов функций в Lua
Вызов функции состоит из имени переменной, содержащей ссылку на функцию, и заключённого в круглые скобки списка аргументов (возможно, пустого).
a = f(2,3) — вызов функции f с двумя аргументами: 2 и 3
— возвращаемое функцией значение помещается в переменную a
Если функции передаётся только один аргумент, и этот аргумент является строковой константой или конструктором таблицы, круглые скобки при вызове функции можно не использовать.
require «myscript» — то же самое, что и require («myscript»)
MsgBox [[Это многострочное значение]] — то же самое, что и MsgBox([[Это многострочное значение]])
render
Ссылка на функцию может быть полем таблицы. В этом случае вызов функции будет выглядеть так:
t.f() — вызов функции, на которую ссылается поле f таблицы t
В Lua имеется возможность вызова функции в объектно-ориентированном стиле:
Таким образом, конструкция t:f() вызывает функцию, на которую ссылается поле f таблицы t, и эта же таблица передаётся в функцию в качестве неявного первого аргумента.
В качестве аргументов функция может принимать несколько значений. Если при вызове функции последние значения не заданы, им присваивается nil. Если значений больше, чем аргументов, «лишние» значения отбрасываются. Аргументы функции являются локальными переменными внутри функции.
function f(x, y) — определение функции
MsgBox («x=»..tostring(x)..»; «..«y=»..tostring(y))
f() — вызов функции. x, y равны nil
f(1) — вызов функции. x = 1, y равен nil
f(1, 2) — вызов функции. x = 1, y = 2
f(1, 2, 3) — вызов функции. x = 1, y = 2, 3 — лишний параметр
Функции с переменным числом аргументов в Lua
Функция может принимать переменное число параметров. Для этого список аргументов в определении функции должен заканчиваться многоточием (. ).
Все значения, скрытые за многоточием, передаются функции через локальную таблицу arg. Поле n этой таблицы содержит число переданных аргументов. Поэтому значения переданных в функцию аргументов можно получить, например, так:
Возврат значений из функции Lua
Функции могут возвращать одно или несколько значений, а также не возвращать значений вообще.
function f0() — функция f0 не возвращает значений
MsgBox («Вызвана функция f0»)
MsgBox («Вызвана функция f1»)
return 1 — функция f1 возвращает одно значение
MsgBox («Вызвана функция f3»)
return 1, 2, 3 — функция f3 возвращает три значения
f0() — вызов функции f0
a = f1() — вызов функции f1. a = 1
a, b, c = f3() — вызов функции f3. a = 1, b = 2, c = 3
Если количество возвращаемых функцией значений превышает число переменных, которым эти значения должны быть присвоены, «лишние» значения отбрасываются. Если возвращаемых значений меньше, чем переменных, отсутствующие значения заменяются nil.
a = f3() — a = 1, значения 2 и 3 отброшены
a, b, c = f3() — a = 1, b = 2, значение 3 отброшено
a, b, c = f0() — a = nil, b = nil, c = nil
a, b, c = f1() — a = 1, b = nil, c = nil
Вне зависимости от того, возвращает функция значения или нет, её можно вызвать как оператор. Все возвращаемые функцией значения в этом случае будут отброшены:
f3() — значения, возвращенные функцией f3, отброшены
Возникают ситуации, когда функция возвращает несколько значений, а переменной требуется присвоить только одно из них. Например, функция возвращает три значения, а для работы нужно только последнее:
Как не задавать лишних имён переменных?
Здесь общепринятым способом является использование переменной с именем «_» (символом подчёркивания). Это упрощает внешний вид выражения и указывает на реально используемые переменные.
local _, _, c = f3() — нам нужно только третье возвращаемое значение
В данном случае, переменная «_» принимает первое, а потом второе возвращаемое значение.
Такой же подход часто используется в работе с итераторами:
for _, value in pairs() do
… — нам нужно только очередное значение, ключ будет присвоен переменной _
Переменная с именем «_» является обычной переменной. При использовании этой переменной в других выражениях, она будет переопределяться, т. е. содержать то значение, которое было ей присвоено в последний раз:
local _, _, c = f3() — переменная _ будет содержать второй параметр
f4(_, true) — первым параметром функуции f4 будет не пустое значение,
а значение содеражащееся в переменной _
Возврат значений при множественном присваивании
При использовании множественного присваивания все возвращаемые функцией значения учитываются только в том случае, если вызов функции является последним (или единственным) выражением в списке выражений справа от оператора присваивания. В противном случае в присваивании участвует только одно (первое) возвращённое функцией значение.
a, b, c, d = 5, f3() — a = 5, b = 1, c = 2, d = 3
a, b, c, d = f3(), 5 — a = 1, b = 5, c = nil, d = nil
Аналогичным образом учитываются результаты вызова функции, включённого в конструктор таблицы, список аргументов другой функции и в список результатов, возвращаемых оператором return.
func (5, f3()) — вызов функции func с аргументами 5, 1, 2, 3
func (f3(), 5) — вызов функции func с аргументами 1, 5
return 5, f3() — возврат значений 5, 1, 2, 3
return f3(), 5 — возврат значений 1, 5
Возврат функций из функций
Функции можно возвращать из функций. Например:
local i = outer() — значение переменной i равно функции inner
local a = i() — вызываем функцию i, значение переменной a равно 1
Базовая библиотека стандартных функций Lua
Функции базовой библиотеки Lua
Функции базовой библиотеки Lua размещаются в глобальном контексте.
В базовую библиотеку входят следующие функции:
Имя функции — Описание
assert- Генерирует ошибку с необязательным сообщением об ошибке, если значение первого аргумента false или nil.
error- Завершает выполнение последней функции, вызванной в защищённом режиме, с заданным сообщением об ошибке.
getfenv — Возвращает таблицу контекста заданной функции. Аргумент, определяющий функцию, может быть как, собственно, Lua-функцией, так и числом, определяющим уровень стека, на котором расположена функция. Если он равен 0, возвращается глобальный контекст.
getmetatable- Если объект не имеет метатаблицы, возвращает nil. Иначе, если в метатаблице есть поле __metatable, возвращает значение этого поля. В противном случае возвращает метатаблицу объекта.
ipairs — Возвращает итератор, таблицу и 0. Возвращаемый итератор проходит таблицу по целочисленным индексам от значения 1 до первого индекса со значением nil. Итератор возвращает текущий индекс и соответствующее ему значение.
next- Возвращает следующий (после заданного) индекс в таблице и соответствующее ему значение. Позволяет последовательно получить все поля таблицы.
pairs — Возвращает итератор next(), таблицу и nil. Возвращаемый итератор проходит таблицу по всем значениям индекса. Итератор возвращает текущий индекс и соответствующее ему значение.
pcall — Вызывает заданную функцию с аргументами в защищённом режиме. Возвращает статус успешности выполнения.
rawequal — Сравнивает два объекта, без вызова каких-либо метаметодов. Возвращает значение типа boolean.
rawget- Возвращает реальное значение с заданным индексом из таблицы, без вызова каких-либо метаметодов.
rawset — Помещает значение в поле таблицы с заданным индексом, без вызова каких-либо метаметодов.
select — Если первый аргумент функции имеет числовое значение, возвращаются все аргументы, следующие за аргументом с этим номером. Если первый аргумент — строка ’#’, возвращается общее число полученных аргументов.
setfenv — Устанавливает таблицу в качестве контекста для заданной функции. Аргумент, задающий функцию, может быть числом, определяющим положение функции на стеке вызовов. Если он равен 0, устанавливается глобальный контекст текущего потока.
setmetatable — Устанавливает (удаляет) метатаблицу для данной таблицы. Если метатаблица содержит поле __metatable, генерирует ошибку.
tonumber — Пытается конвертировать аргумент в число. Если конвертирование не удаётся, возвращает nil.
tostring — Преобразует аргумент любого типа в строку.
type — Возвращает тип аргумента в виде строки.
unpack- Возвращает элементы из заданной таблицы.
xpcall — Вызывает заданную функцию в защищённом режиме. В отличие от pcall, позволяет установить обработчик ошибок. Не поддерживает передачу аргументов в функцию.
Глобальные переменные
Помимо функций, базовая библиотека предоставляет также следующие глобальные переменные:
Имя переменной Описание
_G Таблица, содержащая глобальное окружение скрипта.
_M Таблица, содержащая текущий модуль.
Библиотека для работы со строками Lua
Все функции для работы со строками собраны в таблице string. Поэтому для вызова функций используется запись вида:
Поддерживается также объектно-ориентированная форма записи. Например:
s:trim() — эквивалентно string.trim(s)
В библиотеку для работы со строками входят следующие стандартные функции:
Имя функции- Описание
len- Возвращает длину строки.
rep — Возвращает строку, содержащую указанное число копий исходной строки.
lower — Заменяет все прописные буквы в строке на строчные.
upper — Заменяет в строке все строчные буквы на прописные.
reverse — Возвращает строку, в которой символы исходной строки расположены в обратном порядке.
format- Генерирует строку по форматной строке и аргументам по правилам, принятым в языке C.
byte — Возвращает числовые коды символов строки.
char — Преобразует набор числовых кодов символов в строку.
find — Выполняет поиск в строке первого вхождения подстроки, соответствующей заданному шаблону. Возвращает индексы начального и конечного символов найденной подстроки.
match- Выполняет поиск в строке первого вхождения подстроки, соответствующей заданному шаблону. Возвращает найденную подстроку.
gmatch- Возвращает итератор, который на каждой итерации возвращает подстроку, соответствующую заданному шаблону.
sub — Возвращает подстроку исходной строки.
gsub — Возвращает копию исходной строки, в которой все вхождения шаблона заменены на строку, заданную третьим аргументом. Этот аргумент может быть строкой, таблицей или функцией.
dump- Возвращает строку, содержащую двоичное представление функции Lua с заданным именем.
Библиотека для работы с таблицами Lua
Все функции для работы с таблицами собраны в таблице table. Для вызова функций используется запись вида:
В данную библиотеку включены следующие стандартные функции:
Имя функции- Описание
insert- Вставляет элемент в заданную позицию таблицы, сдвигая остальные элементы таблицы вправо.
remove — Удаляет заданный элемент таблицы, сдвигая остальные элементы влево. Возвращает значение удалённого элемента.
sort- Сортирует элементы таблицы в заданном порядке. Вторым аргументом может быть задана функция, которая будет использована вместо стандартного оператора « ★65