Изучение lua. Смотреть что такое "Lua" в других словарях

  • 14.06.2019

Я сентиментальный программист. Иногда я влюбляюсь в языки программирования, и тогда я могу говорить о них часами. Одним из этих часов я поделюсь с вами.

Lua? Что это?

Lua — простой встраиваемый язык (его можно интегрировать с вашими программами, написанными на других языках), легкий и понятный, с одним типом данных, с однообразным синтаксисом. Идеальный язык для изучения.

Зачем?

Lua может вам пригодится:

* если вы геймер (плагины для World of Warcraft и множества других игр)
* если вы пишете игры (очень часто в играх движок пишут на C/C++, а AI — на Lua)
* если вы системный программист (на Lua можно писать плагины для nmap, wireshark, nginx и других утилит)
* если вы embedded-разработчик (Lua очень быстрый, компактный и требует очень мало ресурсов)

1. Научитесь программировать. Хотя бы немного. Не важно на каком языке.
2. Установите Lua. Для этого либо скачайте здесь версию 5.2 (http://www.lua.org/download.html), либо ищите ее в репозиториях. Версия 5.1 тоже пойдет, но знайте, что она очень старая.

Все примеры из статьи запускайте в терминале командой наподобие «lua file.lua».

Первые впечатления

Lua — язык с динамической типизацией (переменные получают типы «на лету» в зависимости от присвоенных значений). Писать на нем можно как в императивном, так и в объектно-ориентированном или функциональном стиле (даже если вы не знаете как это — ничего страшного, продолжайте читать). Вот Hello world на Lua:

My first lua app: hello.lua print "hello world"; print("goodbye world")

Что уже можно сказать о языке:

* однострочные комментарии начинаются с двух дефисов "--"
* скобки и точки-с-запятыми можно не писать

Операторы языка

Набор условных операторов и циклов довольно типичен:

Условные операторы (ветки else может не быть) if a == 0 then print("a is zero") else print("a is not zero") end -- сокращенная форма if/elseif/end (вместо switch/case) if a == 0 then print("zero") elseif a == 1 then print("one") elseif a == 2 then print("two") else print("other") end -- цикл со счетчиком for i = 1, 10 do print(i) end -- цикл с предусловием b = 5 while b > 0 do b = b - 1 end -- цикл с постусловием repeat b = b + 1 until b >= 5

ПОДУМАЙТЕ: что может означать цикл "for i = 1, 10, 2 do ... end" ?

В выражениях можно использовать такие вот операторы над переменными:

* присваивание: x = 0
* арифметические: +, -, *, /, % (остаток от деления), ^ (возведение в степень)
* логические: and, or, not
* сравнение: >, <, ==, <=, >=, ~= (не-равно, да-да, вместо привычного «!=»)
* конкатенация строк (оператор «..»), напр.: s1=»hello»; s2=»world»; s3=s1..s2
* длина/размер (оператор #): s=»hello»; a = #s (‘a’ будет равно 5).
* получение элемента по индексу, напр.: s

Битовых операций в языке долгое время не было, но в версии 5.2 появилась библиотека bit32, которая их реализует (как функции, не как операторы).

Типы данных

Я вам соврал, когда сказал что у языка один тип данных. Их у него много (как и у каждого серьезного языка):

* nil (ровным счетом ничего)
* булевы числа (true/false)
* числа (numbers) — без деления на целые/вещественные. Просто числа.
* строки — кстати, они очень похожи на строки в паскале
* функции — да, переменная может быть типа «функция»
* поток (thread)
* произвольные данные (userdata)
* таблица (table)

Если с первыми типами все понятно, то что же такое userdata? Вспомним о том, что Lua — язык встраиваемый, и обычно тесно работает с компонентами программ, написанными на других языках. Так вот, эти «чужие» компоненты могут создавать данные под свои нужды и хранить эти данные вместе с lua-объектами. Так вот, userdata — и есть подводная часть айсберга, которая с точки зрения языка lua не нужна, но и просто не обращать внимания на нее мы не можем.

А теперь самое важное в языке — таблицы.

Таблицы

Я вам снова соврал, когда сказал, что у языка 8 типов данных. Можете считать что он один: всё — это таблицы (это, кстати, тоже неправда). Таблица — это очень изящная структура данных, она сочетает в себе свойства массива, хэш-таблицы («ключ»-«значение»), структуры, объекта.

Итак, вот пример таблицы как массива: a = {1, 2, 3} -- массив из 3-х элементов print(a) -- выведет "2", потому что индесы считаются с единицы -- А таблица в виде разреженного массива (у которого есть не все элементы) a = {} -- пустая таблица a = 1 a = 5

ПОДУМАЙТЕ: чему равно a в случае разреженного массива?

В примере выше таблица ведет себя как массив, но на самом деле — у нас ведь есть ключи (индексы) и значения (элементы массива). И при этом ключами могут быть какие угодно типы, не только числа:

A = {} a["hello"] = true a["world"] = false a = 1 -- или так: a = { hello = 123, world = 456 } print(a["hello")) print(a.hello) -- то же самое, что и a["hello"], хотя выглядит как структура с полями

Кстати, раз уж у таблицы есть ключи и значения, то можно в цикле перебрать все ключи и соответствующие им значения:

T = { a = 3, b = 4 } for key, value in pairs(t) do print(key, value) -- выведет "a 3", потом "b 4" end

А как же объекты? О них мы узнаем чуть позже, вначале — о функциях.

Функции

Вот пример обычной функции.

Function add(a, b) return a + b end print(add(5, 3)) -- напечатает "8"

Функции языка позволяют принимать несколько аргументов, и возвращать несколько аргументов. Так аргументы, значения которых не указаны явно, считаются равными nil.

ПОДУМАЙТЕ: зачем может понадобиться возвращать несколько аргументов?

Function swap(a, b) return b, a end x, y = swap(x, y) -- кстати, это можно сделать и без функции: x, y = y, x -- и если уж функция возвращает несколько аргументов, -- а они вам не нужны - игнорируйте их с помощью -- специальной переменной-подчеркивания "_" a, _, _, d = some_function()

Функции могут принимать переменное количество аргументов:

В прототипе переменное число аргументов записывается как троеточие function sum(...) s = 0 for _, n in pairs(arg) do -- в функции обращаются к ним, как к таблице "arg" s = s + n end return a end sum(1, 2, 3) -- вернет 6 sum(1, 2, 3, 4) -- вернет 10

Поскольку функции — это полноценный тип данных, то можно создавать переменные-функции, а можно передавать функции как аргументы других функций

A = function(x) return x * 2 end -- функция, умножающая на 2 b = function(x) return x + 1 end -- функция, увеличивающая на 1 function apply(table, f) result = {} for k, v in pairs(table) do result[k] = f(v) -- заменяем элемент на какую-то функцию от этого элемента end end -- ПОДУМАЙТЕ: что вернут вызовы t = {1, 3, 5} apply(t, a) apply(t, b)

Объекты = функции + таблицы

Раз мы можем сохранять функции в переменных, то и в полях таблиц тоже сможем. А это уже получаются как-бы-методы. Для тех, кто не знаком с ООП скажу, что основная его польза (по крайней мере в Lua) в том, что функции и данные, с которыми они работают находятся рядом — в пределах одного объекта. Для тех, кто знаком с ООП скажу, что классов здесь нет, а наследование прототипное.

Перейдем к примерам. Есть у нас объект, скажем, лампочка. Она умеет гореть и не гореть. Ну а действия с ней можно сделать два — включить и выключить:

Lamp = { on = false } function turn_on(l) l.on = true end function turn_off(l) l.on = false end -- это просто функции для работы со структурой turn_on(lamp) turn_off(lamp)

А если лампочку сделать объектом, и функции turn_off и turn_on сделать полями объекта, то получится:

Lamp = { on = false turn_on = function(l) l.on = true end turn_off = function(l) l.on = false end } lamp.turn_on(lamp) lamp.turn_off(lamp)

Мы вынуждены передавать сам объект лампочки в качестве первого аргумента, потому что иначе наша функция не узнает с какой именно лампочкой надо работать, чтобы сменить состояние on/off. Но чтобы не быть многословными, в Lua есть сокращенная запись, которую обычно и используют — lamp:turn_on(). Итого, мы уже знаем несколько таких упрощений синтаксиса:

Lamp:turn_on() -- самая общепринятая запись lamp.turn_on(lamp) -- то с точки зрения синтаксиса это тоже правильно lamp["turn_on"](lamp) -- и это

Продолжая говорить о сокращениях, функции можно описывать не только явно, как поля структуры, но и в более удобной форме:

Lamp = { on = false } -- через точку, тогда аргумент надо указывать function lamp.turn_on(l) l.on = true end -- через двоеточкие, тогда аргумент неявно задается сам, как переменная "self" -- "self" - и есть та лампочка, для которой вызвали метод function lamp:turn_off() self.on = false end

Интересно?

Специальные функции

Некоторые имена функций таблиц (методов) зарезервированы, и они несут особый смысл:

* __add(a, b), __sub(a, b), __div(a, b), __mul(a, b), __mod(a, b), __pow(a, b) — вызываются, когда выполняются арифметические операции над таблицей
* __unm(a) — унарная операция «минус» (когда пишут что-то типа «x = -x»)
* __lt(a, b), __le(a, b), __eq(a, b) — вычисляют результат сравнения (<, <=, ==)
* __len(a) — вызывается, когда делается "#a"
* __concat(a, b) — вызывается при "a..b"
* __call(a, …) — вызывается при "a()". Переменные аргументы — это аргументы при вызове
* __index(a, i) — обращение к a[i], при условии, что такого элемента не существует
* __newindex(a, i, v) — создание "a[i] = v"
* __gc(a) — когда объект удаляется при сборке мусора

Подменяя эти методы, можно перегружать операторы и использовать синтаксис языка для своих целей. Главное не переусердствовать.

Наследование

Для тех, кто не знает ООП, наследование позволяет расширить функциональность уже существующего класса. Например, просто лампочка умеет включаться-выключаться, а супер-ламкочка будет еще и яркость менять. Зачем нам переписывать методы turn_on/turn_off, если можно их повторно использовать?

В Lua для этого есть понятие мета-таблицы, т.е. таблицы-предка. У каждой таблицы есть одна таблица-предок, и дочерняя таблица умеет делать все, что умеет предок.

Допустим, что объект-таблицу lamp мы уже создали. Тогда супер-лампочка будет выглядеть так:

Superlamp = { brightness = 100 } -- указываем родительскую таблицу setmetatable(superlamp, lamp) -- и ее методы теперь доступны superlamp:turn_on() superlamp:turn_off()

Расширение функциональности

Родительские таблицы есть у многих типов (ну у строк и таблиц точно, у чисел и булевых чисел, и у nil их нет). Допустим, мы хотим складывать все строки с помощью оператора "+" , а не ".." . Для этого надо подменить функцию «+» (__add) для родительской таблицы всех строк:

S = getmetatable("") -- получили родительскую таблицу строки s.__add = function(s1, s2) return s1..s2 end -- подменили метод -- проверяем a = "hello" b = "world" print(a + b) -- напишет "helloworld"

Собственно, мы еще можем заменить функцию print с помощью «print = myfunction», да и много других хакерских дел можно сделать.

Области видимости

Переменные бывают глобальные и локальные. При создании все переменные в Lua являются глобальными.

ПОДУМАЙТЕ: почему?

Для указания локальной области видимости пишут ключевое слово local:

Local x local var1, var2 = 5, 3

Не забывайте об этом слове.

Обработка ошибок

Часто, если возникают ошибки, надо прекратить выполнение определенной функции. Можно, конечно, сделать множество проверок и вызывать «return», если что-то пошло не так. Но это увеличит объем кода. В Lua используется что-то наподобие исключений (exceptions).

Ошибки порождаются с помощью функции error(x). В качестве аргумента можно передать все, что угодно (то, что имеет отношение к ошибке — строковое описание, числовой код, объект, с которым произошла ошибка и т.д.)

Обычно после этой функции вся программа аварийно завершается. А это надо далеко не всегда. Если вы вызываете функцию, которая может создать ошибку (или ее дочерние функции могут создать ошибку), то вызывайте ее безопасно, с помощью pcall():

Function f(x, y) ... if ... then error("failed to do somthing") end ... end status, err = pcall(f, x, y) -- f:функция, x-y: ее аргументы if not status then -- обработать ошибку err. В нашем случае в err находится текст ошибки end

Стандартные библиотеки

Нестандартных библиотек много, их можно найти на LuaForge, LuaRocks и в других репозиториях.

Между Lua и не-Lua

А если нам недостаточно функциональности стандартных библиотек? Если у нас есть наша программа на C, а мы хотим вызывать ее функции из Lua? Для этого есть очень простой механизм.

Допустим, мы хотим создать свою функцию, которая возвращает случайное число (в Lua есть math.random(), но мы хотим поучиться). Нам придется написать вот такой код на C:

#include #include #include /* собственно, что делать при вызове `rand(from, to)` */ static int librand_rand(lua_State *L) { int from, to; int x; from = lua_tonumber(L, 1); /* первый параметр функции */ to = lua_tonumber(L, 2); /* второй параметр функции */ x = rand() % (to - from + 1) + from; lua_pushnumber(L, x); /* возвращаемое значение */ return 1; /* возвращаем только один аргумент */ } /* в Lua "rand" соответствует нашей функции librand_rand() */ static const luaL_reg R = { {"rand", librand_rand}, {NULL, NULL} /* конец списка экспортируемых функций */ }; /* вызывается при загрузке библиотеку */ LUALIB_API int luaopen_librand(lua_State *L) { luaL_openlib(L, "librand", R, 0); srand(time(NULL)); return 1; /* завершаемся успешно */ }

Т.е. Lua предоставляет нам функции для работы с типами данных, для получения аргументов функций и возврата результатов. Функций очень мало, и они довольно простые. Теперь мы собираем нашу библиотеку как динамическую, и можем использовать функцию rand():Понравилось это.

Lua предлагает высокоуровневую абстракцию без потери связи с аппаратурой

В то время как интерпретируемые языки программирования, такие как Perl, Python, PHP и Ruby, пользуются все большей популярностью для Web-приложений (и уже давно предпочитаются для автоматизации задач по системному администрированию), компилируемые языки программирования, такие как C и C++, по-прежнему необходимы. Производительность компилируемых языков программирования остается несравнимой (она уступает только производительности ручного ассемблирования), поэтому некоторое программное обеспечение (включая операционные системы и драйверы устройств) может быть реализована эффективно только при использовании компилируемого кода. Действительно, всегда, когда программное и аппаратное обеспечение нужно плавно связать между собой, программисты инстинктивно приходят к компилятору C: C достаточно примитивен для доступа к "голому железу" (то есть, для использования особенностей какой-либо части аппаратного обеспечения) и, в то же время, достаточно выразителен для описания некоторых высокоуровневых программных конструкций, таких как структуры, циклы, именованные переменные и области видимости.

Однако языки сценариев тоже имеют четкие преимущества. Например, после успешного переноса интерпретатора языка на другую платформу подавляющее большинство написанных на этом языке сценариев работает на новой платформе без изменений, не имея зависимостей, таких как системные библиотеки функций (представьте множество DLL-файлов операционной системы Microsoft® Windows® или множество libcs на UNIX® и Linux®). Кроме того, языки сценариев обычно предлагают высокоуровневые программные конструкции и удобные операции, которые программистам нужны для повышения продуктивности и скорости разработки. Более того, программисты, использующие язык сценариев, могут работать быстрее, поскольку этапы компиляции и компоновки не нужны. В сравнении с С и его родственниками цикл "кодирование, компоновки, связывание, запуск" сокращается до ускоренного "написание, запуск".

Новшества в Lua

Как и любой язык сценариев, Lua имеет свои особенности:

  • Типы в Lua . В Lua значения имеют тип, но переменные типизируются динамически. Типы nil , boolean , number и string работают так, как вы могли бы ожидать.
    • Nil - это тип специального значения nil ; используется для представления отсутствия значения.
    • Boolean - это тип констант true и false (Nil тоже представляет значение false , а любое не nil значение представляет true).
    • Все числа в Lua имеют тип doubles (но вы можете легко создать код для реализации других числовых типов).
    • string - это неизменяемый массив для символов (следовательно, для добавления к строке вы должны сделать ее копию).
  • Типы table , function и thread являются ссылками. Каждый такой тип может быть назначен переменной, передаваемой в качестве аргумента, или возвращаемой из функции. Ниже приведен пример сохранения функции:

    Пример анонимной функции, -- возвращаемой как значение -- см. http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/hopl.pdf function add(x) return function (y) return (x + y) end end f = add(2) print(type(f), f(10)) function 12

  • Потоки в Lua . Поток - это сопрограмма, создаваемая вызовом встроенной функции coroutine.create(f) , где f - это функция Lua. Потоки не запускаются при создании; они запускаются позже при помощи функции coroutine.resume(t) , где t - это поток. Каждая сопрограмма может время от времени отдавать процессор другим сопрограммам при помощи функции coroutine.yield() .
  • Выражения присваивания . Lua разрешает множественные присваивания, и выражения сначала вычисляются, а затем присваиваются. Например, результат выражений

    I = 3 a = {1, 3, 5, 7, 9} i, a[i], a, b = i+1, a, a[i] print (i, a, a, b, I)
    равен 4 7 5 nil nil . Если список переменных больше, чем список значений, лишним переменным присваивается значение nil ; поэтому b равно nil . Если значений больше, чем переменных, лишние значения просто игнорируются. В Lua названия переменных зависят от регистра символов, что объясняет, почему переменная I равна nil .

  • Порции (chunks) . Порцией называется любая последовательность Lua-операторов. Порция может быть записана в файл или в строку в Lua-программе. Каждая порция выполняется как тело анонимной функции. Следовательно, порция может определять локальные переменные и возвращать значения.
  • Дополнительные интересные возможности . Lua имеет сборщик мусора "отметь и выкинь". В Lua 5.1 сборщик мусора работает в инкрементном режиме. Lua имеет полное лексическое замыкание (как Scheme, но не как Python). Кроме того, Lua имеет надежную семантику последовательных вызовов (tail call) (опять же, как Scheme, но не как Python).

Большее количество примеров Lua-кода приведено в руководстве "Программирование в Lua " и в wiki Lua-пользователей (ссылки приведены в разделе " ").

Как всегда, выбор между компилируемым и интерпретируемым языком заключается в сравнении всех за и против каждого языка в контексте, оценке альтернатив и поиске компромиссов.

Беря все лучшее из обоих миров

Что, если бы вы могли взять лучшее из обоих миров: производительность работы с "голым железом" и высокоуровневые, мощные абстракции? Более того, если бы вы могли оптимизировать алгоритмы и функции, зависящие от системы и требующие много процессорного времени, так же как и отдельную логику, не зависящую от системы и очень чувствительную к изменениям требований?

Баланс требований для высокопроизводительного кода и высокоуровневого программирования является сутью Lua, встраиваемого языка программирования. Приложения, включающие Lua, представляют собой комбинацию компилируемого кода и Lua-сценариев. Компилируемый код может при необходимости заняться железом, и, в то же время, может вызывать Lua-сценарии для обработки сложных данных. И поскольку Lua-сценарии отделены от компилируемого кода, вы можете изменять сценарии независимо от него. С Lua цикл разработки более похож на "Кодирование, компоновка, запуск, создание сценариев, создание сценариев, создание сценариев …".

Например, на странице "Uses" Web-сайта Lua (см. раздел " ") перечислены некоторые компьютерные игры для массового рынка, включая World of Warcraft и Defender (версия классической аркады для бытовых консолей), которые интегрируют Lua для запуска всего, начиная с пользовательского интерфейса и заканчивая искусственным интеллектом противника. Другие приложения Lua включают в себя механизмы расширения для популярного инструментального средства обновления Linux-приложений apt-rpm и механизмы управления чемпионатом Robocup 2000 "Сумасшедший Иван". На этой странице есть много хвалебных отзывов о маленьком размере и отличной производительности Lua.

Начало работы с Lua

Lua версии 5.0.2 на момент написания данной статьи была текущей версией (недавно появилась версия 5.1). Вы можете загрузить исходный код Lua с lua.org, а можете найти различные предварительно откомпилированные двоичные файлы на wiki Lua-пользователей (ссылки приведены в разделе " "). Полный код ядра Lua 5.0.2, включая стандартные библиотеки и Lua-компилятор, по размерам не превышает 200KB.

Если вы работаете на Debian Linux, то можете быстро и просто установить Lua 5.0 при помощи следующей команды

# apt-get install lua50

с правами суперпользователя. Все приведенные здесь примеры запускались на Debian Linux "Sarge" с использованием Lua 5.0.2 и ядра Linux 2.4.27-2-686.

После установки Lua на вашей системе попробуйте автономный Lua-интерпретатор. Все Lua-приложения должны быть встроены в базовое приложение. Интерпретатор - это просто специальный тип базового приложения, используемого для разработки и отладки. Создайте файл factorial.lua и введите в него следующие строки:

-- определяет функцию факториала function fact (n) if n == 0 then return 1 else return n * fact(n-1) end end print("enter a number:") a = io.read("*number") print(fact(a))

Код в factorial.lua (точнее, любая последовательность Lua-операторов) называется порцией (chunk), как было описано выше в разделе " ". Для запуска созданной вами порции выполните команду lua factorial.lua:

$ lua factorial.lua enter a number: 10 3628800

Или, как в других языках сценариев, вы можете добавить строку со знаками (#!) ("shebang") в начало сценария, делая сценарий исполняемым, а затем запустить файл как автономную команду:

$ (echo "#! /usr/bin/lua"; cat factorial.lua) > factorial $ chmod u+x factorial $ ./factorial enter a number: 4 24

Язык Lua

Lua обладает многими удобствами, имеющимися в современных языках программирования сценариев: область видимости, управляющие структуры, итераторы и стандартные библиотеки для обработки строк, выдачи и сбора данных и выполнения математических операций. Полное описание языка Lua приведено в "" (см. раздел " ").

В Lua тип имеют только значения , а переменные типизируются динамически. В Lua есть восемь фундаментальных типов (или значений): nil , boolean , number , string , function , thread , table и userdata . Первые шесть типов говорят сами за себя (исключения приведены в разделе " "); два последних требуют пояснения.

Таблицы в Lua

Таблицы - это универсальная структура данных в Lua. Более того, таблицы - это единственная структура данных в Lua. Вы можете использовать таблицу как массив, словарь (называемый также хеш-таблицей или ассоциативным массивом ), дерево, запись и т.д.

В отличие от других языков программирования, содержимое таблицы в Lua не обязательно должно быть однородным: таблица может включать любые типы и может содержать смесь элементов, подобных массиву, и элементов, подобных словарю. Кроме того, любое Lua-значение (в том числе, функция или другая таблица) может служить ключом элемента словаря.

Для исследования таблиц запустите Lua-интерпретатор и введите строки, показанные жирным шрифтом в листинге 1.

Листинг 1. Экспериментируя с таблицами Lua
$ lua > -- создать пустую таблицу и добавить несколько элементов > t1 = {} > t1 = "moustache" > t1 = 3 > t1["brothers"] = true > -- создать таблицу и определить элементы (употребляется чаще) > all at once > t2 = { = "groucho", = "chico", = "harpo"} > t3 = { = t2, accent = t2, horn = t2} > t4 = {} > t4 = "the marx brothers" > t5 = {characters = t2, marks = t3} > t6 = {["a night at the opera"] = "classic"} > -- создать ссылку и строку > i = t3 > s = "a night at the opera" > -- индексами могут быть любые Lua-значения > print(t1, t4, t6[s]) moustache the marx brothers classic > -- фраза table.string эквивалентна фразе table["string"] > print(t3.horn, t3["horn"]) harpo harpo > -- индексы могут быть также "многомерными" > print (t5["marks"]["horn"], t5.marks.horn) harpo harpo > -- i указывает на то же значение, что и t3 > = t4[i] the marx brothers > -- несуществующие индексы возвращают значения nil > print(t1, t2, t5.films) nil nil nil > -- даже функция может быть ключом > t = {} > function t.add(i,j) >> return(i+j) >> end > print(t.add(1,2)) 3 > print(t["add"](1,2)) 3 > -- и другой вариант функции в качестве ключа > t = {} > function v(x) >> print(x) >> end > t[v] = "The Big Store" > for key,value in t do key(value) end The Big Store

Как вы могли ожидать, Lua также предоставляет несколько функций-итераторов для обработки таблиц. Функции предоставляет глобальная переменная table (да, Lua-пакеты - это тоже просто таблицы). Некоторые функции, например table.foreachi() , ожидают непрерывный диапазон целых ключей, начиная с 1 (цифра один):

> table.foreachi(t1, print) 1 moustache 2 3

Другие, например table.foreach() , выполняют итерацию по всей таблице:

> table.foreach(t2,print) 1 groucho 3 chico 5 harpo > table.foreach(t1,print) 1 moustache 2 3 brothers true

Хотя некоторые итераторы оптимизированы для целых индексов, все они просто обрабатывают пары (ключ, значение).

Ради интереса создайте таблицу t с элементами {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="www.lua.org"} и выполните команды table.foreach(t, print) и table.foreachi(t, print) .

Userdata

Поскольку Lua предназначен для встраивания в базовое приложение, написанное на таких языках, как, например, C или C++, для взаимодействия с базовым приложением данные должны совместно использоваться средой C и Lua. Как указано в "Справочном руководстве по Lua 5.0 ", тип userdata позволяет "произвольным C-данным храниться в Lua-переменных". Вы можете рассматривать тип userdata как массив байтов - байтов, которые могут представлять указатель, структуру или файл в базовом приложении.

Содержимое userdata происходит от C, поэтому оно не может быть модифицировано в Lua. Естественно, поскольку userdata происходит от C, в Lua не существует предопределенных операций для userdata. Однако вы можете создать операции, которые работают с userdata , используя еще один механизм Lua, называемый мета-таблицами (metatables).

Мета-таблицы

Из-за такой гибкости типов table и userdata Lua разрешает перегружать операции для объектов каждого из этих типов (вы не можете перегружать шесть остальных типов). Мета-таблица - это (обычная) Lua-таблица, которая отображает стандартные операции в предоставляемые вами пользовательские функции. Ключи мета-таблицы называются событиями (event); значения (другими словами, функции) называются мета-методами (metamethod).

Функции setmetatable() и getmetatable() изменяют и запрашивают мета-таблицу объекта соответственно. Каждый объект table и userdata может иметь свою собственную мета-таблицу.

Например, одним из событий является __add (для добавления). Можете ли вы определить, что делает следующая порция?

-- Перегрузить операцию add -- для конкатенации строк -- mt = {} function String(string) return setmetatable({value = string or ""}, mt) end -- Первый операнд - это String table -- Второй операнд - это string -- .. - это операция конкатенации в Lua -- function mt.__add(a, b) return String(a.value..b) end s = String("Hello") print((s + " There " + " World!").value)

Эта порция отображает следующий текст:

Hello There World!

Функция function String() принимает строку (string), заключает ее в таблицу ({value = s or ""}) и назначает мета-таблицу mt этой таблице. Функция mt.__add() является мета-методом, добавляющим строку b к строке, находящейся в a.value b раз. Строка print((s + " There " + " World!").value) активизирует мета-метод дважды.

Index - это еще одно событие. Мета-метод для __index вызывается всегда, когда ключ в таблице не существует. Вот пример, который запоминает ("memoizes") значение функции:

-- код, любезно предоставленный Рики Лэйком (Rici Lake), [email protected] function Memoize(func, t) return setmetatable(t or {}, {__index = function(t, k) local v = func(k); t[k] = v; return v; end }) end COLORS = {"red", "blue", "green", "yellow", "black"} color = Memoize(function(node) return COLORS end)

Поместите этот код в Lua-интерпретатор и введите print(color, color, color) . Вы должны увидеть что-то подобное blue black blue .

Этот код, получающий ключ и узел, ищет цвет узла. Если он не существует, код присваивает узлу новый, выбранный случайно цвет. В противном случае возвращается цвет, назначенный узлу. В первом случае мета-метод __index выполняется один раз для назначения цвета. В последнем случае выполняется простой и быстрый поиск в хеш-таблице.

Язык Lua предлагает много мощных функциональных возможностей, и все они хорошо документированы. Но всегда, когда вы столкнетесь с проблемами или захотите пообщаться с мастером, обратитесь за поддержкой к энтузиастам - IRC-канал Lua Users Chat Room (см. раздел " ").

Встроить и расширить

Кроме простого синтаксиса и мощной структуры таблиц, реальная мощь Lua очевидна при использовании его совместно с базовым языком. Как уже говорилось, Lua-сценарии могут расширить собственные возможности базового языка. Но справедливо также и обратное - базовый язык может одновременно расширять Lua. Например, C-функции могут вызывать Lua-функции и наоборот.

Сердцем симбиотического взаимодействия между Lua и его базовым языком является виртуальный стек . Виртуальный стек (как и реальный) является структурой данных "последний вошел - первый вышел" (last in-first out - LIFO), которая временно сохраняет аргументы функции и ее результаты. Для вызова из Lua базового языка (и наоборот) вызывающая сторона помещает значения в стек и вызывает целевую функцию; принимающая сторона достает аргументы из стека (конечно же, проверяя тип и значение каждого аргумента), обрабатывает данные и помещает в стек результаты. Когда управление возвращается вызывающей стороне, она извлекает значения из стека.

Фактически, все С-интерфейсы прикладного программирования (API) для Lua-операций работают через стек. Стек может хранить любое Lua-значение; однако тип значения должен быть известен как вызывающей стороне, так и вызываемой, а конкретные функции помещают в стек и извлекают из него каждый тип (например, lua_pushnil() и lua_pushnumber()).

В листинге 2 показана простая C-программа (взятая из главы 24 книги "Программирование в Lua ", ссылка на которую приведена в разделе " "), реализующая минимальный, но функциональный Lua-интерпретатор.

Листинг 2. Простой Lua-интерпретатор
1 #include 2 #include 3 #include 4 #include 5 6 int main (void) { 7 char buff; 8 int error; 9 lua_State *L = lua_open(); /* открывает Lua */ 10 luaopen_base(L); /* открывает основную библиотеку */ 11 luaopen_table(L); /* открывает библиотеку table */ 12 luaopen_io(L); /* открывает библиотеку I/O */ 13 luaopen_string(L); /* открывает библиотеку string */ 14 luaopen_math(L); /* открывает библиотеку math */ 15 16 while (fgets(buff, sizeof(buff), stdin) != NULL) { 17 error = luaL_loadbuffer(L, buff, strlen(buff), "line") || 18 lua_pcall(L, 0, 0, 0); 19 if (error) { 20 fprintf(stderr, "%s", lua_tostring(L, -1)); 21 lua_pop(L, 1); /* извлечь сообщение об ошибке из стека */ 22 } 23 } 24 25 lua_close(L); 26 return 0; 27 }

Строки с 2 по 4 включают стандартные Lua-функции, несколько удобных функций, используемых во всех Lua-библиотеках, и функции для открытия библиотек, соответственно. Строка 9 создает Lua-структуру . Все структуры сначала пусты; вы добавляете библиотеки или функции к структуре при помощи luaopen_...() , как показано в строках с 10 по 14.

В строке 17 luaL_loadbuffer() принимает входную информацию с stdin в виде порции и компилирует ее, помещая порцию в виртуальный стек. Строка 18 извлекает порцию из стека и выполняет ее. Если во время исполнения возникает ошибка, Lua-строка помещается в стек. Строка 20 обращается к вершине стека (вершина стека имеет индекс -1) как к Lua-строке, распечатывает сообщение и удаляет значение из стека.

Используя C API, ваше приложение может также "достать" информацию из Lua-структуры. Следующий фрагмент кода извлекает две глобальные переменные из Lua-структуры:

.. if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) error(L, "cannot run configuration file: %s", lua_tostring(L, -1)); lua_getglobal(L, "width"); lua_getglobal(L, "height"); .. width = (int) lua_tonumber(L, -2); height = (int) lua_tonumber(L, -1); ..

Опять же, обратите внимание на то, что передачу разрешает стек. Вызов любой Lua-функции из C аналогичен следующему коду: извлечь функцию при помощи lua_getglobal() , поместить аргументы, выполнить lua_pcall() и обработать результаты. Если Lua-функция возвращает n значений, первое значение находится по индексу -n в стеке, а последнее - по индексу -1 .

Обратное действие (вызов C-функции из Lua) аналогично. Если ваша операционная система поддерживает динамическую загрузку, Lua может загружать и вызывать функции по требованию. В операционных системах, в которых необходима статическая загрузка, расширение Lua-механизма для вызова C-функции потребует перекомпоновки Lua.

Lua великолепен

Lua - это чрезвычайно легкий в использовании язык, но его простой синтаксис маскирует его мощь: язык поддерживает объекты (аналогичные объектам Perl), мета-таблицы делают его тип table абсолютно гибким, а C API разрешает отличную интеграцию и расширение сценариев и базового языка. Lua может использоваться совместно с языками C, C++, C#, Java™ и Python.

Перед созданием еще одного формата конфигурационного файла или ресурса (и еще одного синтаксического анализатора для него) попробуйте Lua. Язык Lua (так же как и его сообщество) надежен, изобретателен и готов прийти на помощь.

LUÁ, iau, vb. I. tranz. I. 1. A prinde un obiect în mână spre a l ţine (şi a se servi de el) sau spre a l pune în altă parte. ♢ expr. A lua altă vorbă = a schimba (cu dibăcie) subiectul unei discuţii. A(şi) lua picioarele la spinare = a pleca… … Dicționar Român

Lua - Logo Basisdaten Paradigmen: Skriptsprache, imperativ, funktional, objektorientiert … Deutsch Wikipedia

Lua - [] Información general Paradigma Multiparadigma: interpretado, imperativo, funcional, orientado a objetos, basado en prototipos Apareció en … Wikipedia Español

LUA - Apparu en 1993 Auteur Luiz Henrique de Figueiredo, Roberto Ierusalimschy et Waldemar Celes Implémentations Lua, LuaJIT, LLVM Lua, LuaCLR, Nua, Lua A … Wikipédia en Français

LUA - (portugiesisch für Mond) ist eine Skriptsprache zum Einbinden in Programme, um diese leichter weiterentwickeln und warten zu können. Eine der besonderen Eigenschaften von Lua ist die geringe Größe des kompilierten Skript Interpreters. Lua wurde… … Deutsch Wikipedia

lua - s. f. 1. O único planeta satélite da Terra. 2. Tempo compreendido entre dois novilúnios. 3. Mês. 4. Cio. 5. O mesmo que peixe lua. 6. Disco de ouro ou prata que os Timores usam ao pescoço, como símbolo de… … Dicionário da Língua Portuguesa

Lua - may refer to: * Lua (programming language), a lightweight, extensible programming language * Lua (Yu Gi Oh! 5D s) * Lua (goddess), the Roman goddess * Lua (martial art), a traditional Hawaiian martial art * Lua (song), a single by the folk rock… … Wikipedia

LUA аббревиатура: LUA последний всеобщий предок (также переводится как «Последний универсальный предок» (англ. LUA, Last Universal Ancestor), иначе Последний универсальный общий предок (англ. LUCA, last universal common… … Википедия

lúa - (Del gót. lôfa, palma de la mano). 1. f. Especie de guante hecho de esparto y sin separaciones para los dedos, que sirve para limpiar las caballerías. 2. Mar. Revés de las velas por la parte donde van cazadas con viento largo o en popa. 3. Mar.… … Diccionario de la lengua española

Lua - Lua, römische Göttin, Tochter des Saturnus, welcher nach der Schlacht zur Sühne des vergossenen Blutes erbeutete Waffen von dem Feldherrn verbrannt wurden … Pierer"s Universal-Lexikon

LUA - Dea quaedam apud vett, a luendo, expiandoque nomen sortita, quae praeerat lustrationibus, et lustris. Vide Turneb. Adver s. l. 16. c. 20. et l. 23. c. 23. et l. 19. c. 11. Eius meminit A. Gell. l. 13. c. 22. cum ait in libris Sacerdotum Pop. Rom … Hofmann J. Lexicon universale

Книги

  • Программирование на языке Lua , Иерузалимски Роберту. Книга посвящена одному из самых популярных встраиваемых языков - Lua. Этот язык использовался во многих играх и большом количестве различных приложений. Язык сочетает небольшой объем…
  • Программирование на языке Lua. 3-е издание , Иерузалимски Р.. Книга посвящена одному из самых популярных встраиваемых языков - Lua. Этот язык использовался во многих играх и большом количестве различных приложений. Язык сочетает небольшой объем…

Всем привет.

Сегодня мы поверхностно пройдёмся по языку Lua, его некоторым возможностям, а так же запуске наших сценариев в RakBot.
Lua - скриптовый язык программирования, предназначен для быстрой обработки данных. С помощью данного языка многие разработчики создают искусственный интелект в играх, пишут алгоритмы генерации уровней, а так же он используется для разработки ресурсов/игровых модов в Multi Theft Auto: San Andreas (аналог SA:MP). На самом деле, это простейший язык и с помощью него мы будем учиться писать собственную логику для ботов, которую будет использовать RakBot.

Пройдёмся по основам программирования, с которыми нам предстоит работать.

Обратите внимание : данная статья будет урезана в плане языка Lua, так как в RakBot используется лишь небольшая её часть. Многие возможности Lua попросту отсустствуют в RakBot, поэтому я буду ориентироваться на версию из RakBot.

Есть традиция у всех авторов книг и документаций различных языков, это первая программа, которая печатает "Hello World".
Чтож, давайте попробуем написать её, но уже в RakBot. Переходим на оффициальный сайт RakBot и ищем раздел "Доступные функции", раздел "События".

Нам необходимо событие onScriptStart() , которые вызывается автоматически при загрузке скрипта самим RakBot"ом.

В этой функции нам необходимо описать логику, которая будет писать в чат-лог RakBot"a "Hello World". Для этого, на той же странице в документации, посмотрим на раздел "Функции".

Первая фукнция printLog(text) - это то, что нам и нужно. С помощью этой функции мы отправим сообщение в чат RakBot"а. Для этого мы напишем:

Мы написали логику в каком-то текстовом документе, но как сказать RakBot, чтобы он выполнил наш сценарий? Для этого необходимо сохранить файл с расширением .lua и положить его в папку scripts , в папке с RakBot.
Я сохранил текстовый документ с именем "example.lua ". Давайте попробуем запустить RakBot и посмотреть, что у нас получилось.

Как мы видим, при запуске RakBot, он находит скрипт "example.lua ", после чего выполняет его. Из этого мы можем сделать вывод, что инициализация сценария происходит при запуске самого RakBot или при перезагрузке всех сценариев командой !reloadscripts .

Поздравляю, Вы только что написали свой собственный сценарий для RakBot!

Мы уже научились писать Hello World в консоли RakBot"а, но мы хотим писать сложных ботов, которые будут делать всю работу за нас, учитывая те или иные условия. На этом мы остановимся.
Практически всё, что происходит в программировании, можно описать следующим образом: возьми данные, что-то с ними сделай, отдай результат.
В данном случае данными выступает сам RakBot. Он сам запускает наши сценарии, а так же сам передаёт нам данные, которые мы можем обработать так, как хотим и в конце получить результат.

Давайте напишем простейший сценарий с условием. Условием будет являться ник бота. Если ник бота "СМaster", значит мы выведем в чат RakBot"а "CM FOREVER", если же ник бота совершенно другой - выведем в чат "Nonamer".
Для этого нам поможет условный оператор if else , он же оператор ветвления. Он принимает на себя условие, которое должно вернуть либо true , либо false . Если условие равно true , тогда код внутри будет выполнен, если false - не будет выполнен.
На этом строится большая часть логики любого приложения. Дословно if переводится как "ЕСЛИ", then - "ЗНАЧИТ", else - "ИНАЧЕ" Если это сильно сложно - не переживайте, Вы поймёте всё дальше.

В Lua есть следующие операторы сравнения:
> Больше
< Меньше
>= Больше или равно
<= Меньше или равно
~= Не равно
== Равно

Если мы напишем "CMaster " == "CM " - у нас будет значение False , то есть, ложь
Если мы напишем "CMaster " == "CMaster " - у нас будет значение True , то есть, истина.

5 > 10 -- ложь 5 < 10 -- истина 10 ~= 15 -- истина 10 >= 5 -- истина

Попробуем использовать логику ветвления в нашем предыдущем сценарии.

Тот код, который мы писали ранее:

Function onScriptStart() printLog("Hello world!"); end

Преобразуем следующим образом:

Function onScriptStart() botName = getNickName() if(botName == "CMaster") then printLog("CM FOREVER"); else printLog("Nonamer"); end end

Давайте разберём этот код начиная сверху. Я советую сразу начинать учить читать код. Поэтому попробуем прочитать, что у нас получилось

Function onScriptStart() - создаём фукнцию с именем onScriptStart botName = getNickName() - в переменную botName записываем имя бота if(botName == "CMaster") then - если имя бота равно "CMaster", значит printLog("CM FOREVER"); - пишем в чат "CM Forever". else- ИНАЧЕ, или же если имя бота НЕ РАВНО "CMaster" printLog("Nonamer"); - пишем в чат "Nonamer" end- конец условий end- конец функции

Давайте попробуем проверить код, который мы написали. Я сохранил измененный код так же под именем "example.lua " и запустил RakBot с ником "Mason_Bennett ".

После загрузки нашего сценария, RakBot написал в чат Nonamer. Попробуем зайти с ником "CMaster ".

Как мы видим, наше условие успешно работает и мы видим в чате то, что и хотели.

Пройдёмся немного по переменным. У Вас есть лист бумаги и Вы хотите его сохранить. Сохранить каким образом - куда-то положить, чтобы не потерять его. Например, мы можем положить наш лист бумаги в шкафчик и достать тогда, когда нам будет необходимо. Если у нас будет новый листок и нам не нужен будет старый - мы выкинем старый листок и положим новый.
Это и есть логика переменной. Мы можем создавать переменную с именами, которыми хотим и записывать в них значения, что мы и сделали в предыдущем примере с переменной botName.

В Lua мы можем записывать в переменную всё, что мы хотим. Например, я хочу создать переменную с именем PaperList и записать в неё текст "Lua - урок №2 ". Для этого я напишу:

PaperList = "Lua - урок №1"

Что мы здесь сделали? Написали имя и использовали оператор присваивания "=" и теперь я могу использовать эту переменную в любом месте своего сценария.
Думаю, что если Вы вспомните математику на уровне максимум 5 класса - тут будет всё понятно.

У Lua есть несколько типов переменных, это nil, boolean, number, string . Не бойтесь, это всё очень просто.

На самом деле их несколько больше, но я уже говорил, что в RakBot большая часть функционала отсутствует.

nil - отсуствие значения.
boolean - логические значения, принимает два варианта значений - либо true, либо false.
number - вещественное число с двойной точностью. В Lua нет целочисленного типа, поэтому он выступает в качестве и вещественного и целочисленного типа.
string - строка, здесь, я думаю, всё понятно.
Чтож, давайте попробуем создать несколько переменных и "поиграться" с ними.

number = 0; - создаём переменную с именем number и присваиваем значение 0
number = number + 5; - присваивание значения переменной number + 5 (то есть, 0 + 5), теперь у нас хранится здесь число 5.
number ++; - ++ - инкремент. Другими словами - вы берёте переменную и увеличиваете её на одну единицу. То есть (5 + 1) - теперь 6 лежит у нас в переменной number.
number --; - -- декремент. Другими словами - уменьшаем на одну единицу. (6 - 1) - теперь значение равно 5.

string = "Hello" - создаём переменную string со значением "Hello"
string = string .. "," - конкатенация строк, оно же сложение строк. Что мы здесь сделали? Указани имя переменной, указали оператор конкатенации ".. ", после чего указали ещё одну строку, которую необходимо добавить к первой. Теперь у нас в переменной "string " лежит значение "Hello,".
string = string .. getNickName () - теперь, к "Hello," мы добавили ник бота, пускай будет "Michel". Теперь у нас в переменной string лежит значение "Hello,Michel".

boolean = true ; - создаём переменную boolean со значением true (ИСТИНА).
boolean = getNickName () == "Dimosha" - сравниваем имя бота со строкой Dimosha. Так как имя бота у нас Michel, из предыдущего примера, сюда запишется значение false (ЛОЖЬ).

Немного о функциях. Есть функции, которые возвращают значения, а есть те, которые не возвращают значение. Как Вы успели заметить, наша функция onScriptStart не возвращает значения, а просто выполняет код, который указан внутри.
Мы можем создавать собственные функции для того, чтобы изолировать часть логики из метода и выполнять те или иные операции.
Фукнции так же могут принимать на себя значения, а могут и не принимать.

Давайте пройдёмся по самому простому пути: фукнция без параметров и без возвращаемого значения, которая будет складывать 5 + 10 и выводить результат в консоль RakBot"а.

Я создам функцию с именем Add :

Function Add() -- Создаём фукнцию Add printLog(5 + 10) -- используем метод RakBot для вывода в консоль end-- Конец функции

Мы создали не совсем универсальную фукнцию по двум причинам:
- если мне нужно будет складывать другие числа - мне придётся создавать ещё одну такую же фукнцию
- я не могу использовать полученное значение за пределами фукнции

Попробуем исправить первый недочёт. Для этого мы добавим два параметра в фукнцию, которые будут принимать на себя числа для сложения. Сделаем мы это следующим образом:

Function Add(a, b) printLog(5 + 10) end

Теперь в методе у нас доступны два значения, которые содержатся в двух новых переменных a и b, но на консоль у меня всё равно выводится 15. Исправим это:

Function Add(a, b) printLog(a + b) end

Идеально. Теперь, при вызове этого метода, мы будем получать результат сложения в консоли. Попробуем протестировать. Изменим наш код в example.lua на следующий:

Function Add(a, b) printLog(a + b) end function onScriptStart() Add(5, 10); Add(123, 4324); Add(555, 111); end

И попробуем запустить RakBot. Посмотрим, что из этого получится:

Это решило нашу первую проблему. Попробуем решить вторую, чтобы наша функция возвращала результат.

Перепишем фукнцию Add :

Function Add(a, b) return a + b end

return - ключевое слово для возвращения значения из функции. Перепишем теперь метод onScriptStart :

Function onScriptStart() printLog("Первое значение: "..Add(5, 10)); printLog("Второе значение: "..Add(123, 4324)); printLog("Третье значение: "..Add(555, 111)); end

Посмотрим, что получилось.

Мы могли создать три переменные, присвоив им значения из фукнций Add и после их передавать в метод printLog , но я не стал этого делать, так как код выглядит более читабельным и приятнее.

Теперь, мы научились создавать собственные фукнции с параметрами, без параметров и возвращать из них значения. Я считаю, что этих основ Вам хватит сполна, чтобы написать собственного бота в рамках RakBot"а. В следующих уроках мы будем создавать простейшего бота, которого будем постепенно усложнять, добавляя всё новые и новые возможности и фукнции.

Как это будет выглядеть? Конечно не в виде справки, лишь при изучении какой-то сложной функции будет цитироваться русскоязычная справка по луа с сайта Lua.ru, материал я постараюсь преподнести в форме задача — ее решение, начиная с простого и продвигаясь дальше. Не думаю что за платформу для изучения стоит брать какой-либо хаб, так что мы будем использовать пакет LuaAIO (all in one), с помощью которого мы будем проверять код на луа. Скачать пакет можно , там же лежит справка по пакету, она полностью на английском языке и как-то странно собрана что просто не работает, рабочую версию подготовил alex82 , за что ему огромное спасибо, вот . В этой справке есть мануал по луа, первая редакция книги Programming in Lua в электронном виде и справка по библиотекам, включенным в набор LuaAIO.

Итак, скачали, распаковали, теперь запускаем файл lua.exe, открылось окно редактора, написанного на луа с применением библиотек из пакета луааио, в это углубляться не будем, скажу лишь что файл что мы запустили — среда исполнения кода, сам код находится в файле с таким же именем, но с расширением.lua, в нашем случае это lua.lua.

Начем конечно же с банального примера вывода в консоль «Hello world!», на луа это весьма простой код:

print ("Hello world!" )

Теперь разберемся с тем как посмотреть результат работы скрипта, для начала нам надо его сохранить, для этого выбираем File -> Save или нажимаем Ctrl+S, выбираем имя файла, указывать можно любое, к примеру, example1.lua. Следующим шагом будет запуск нашей программы, тот редактор в котором мы работаем позволяет делать это прямо в нем, так что выбираем в верхнем меню Debug -> Run или нажимаем просто F5, видим появившееся внизу окно, в котором отобразился следующий текст:

> Program example1.lua is running
Hello world!

Вот он, результат работы! Теперь останавливаем скрипт выбрав Debug -> Stop или нажав F6, к тексту дописалось

> Program example1.lua is stopped

что означает что скрипт остановлен (теперь можно свернуть до самого низа это окно, оно вылезет само при следующем запуске). А вот сейчас уже разберем сам скрипт на запчасти, узнав что в нем означает каждый символ. Сразу скажу что углубляться в фишки и разнообразные уловки написания скриптов я не буду, поэтому код идет в стандартном (в моем понимании) виде и содержит обычне конструкции. И еще одно небольшое отступление, если вы не владеете логикой, не можете составить план работы и продумать исполнение самого скрипта, то лучше и не браться, а уж если взялись — стараться разобраться самому что да как, а не доставать не очень добрых дядек программистов, они очень сильно начинают нервничать когда пытаются вам на пальцах объяснить что надо сделать и почему именно так, а вы в это время ничего не понимаете или просто «тупите». Если все-таки упрямость играет в вас и вы еще не закрыли этот текст, тогда продолжим. Продолжим с еще одного важного как мне кажется отступления, заключается оно в том, что здешний материал — мое видение, в том числе и русского языка, а не только луа. Любые замечания по этому тексту будут учитываться, но постарайтесь не вступать в спор из-за какой-либо не нравящейся вам запятой или конструкции в коде. Я не мастер луа, но кое-что умею, об этом и постараюсь рассказать.

И снова продолжим (обожаю такие отступления, они заставляют и тебя и собесеников держать нить разговора))), начнем с того как выглядит вызов функции в луа. Функция — некий набор инструкций, выполняющийся по вызову. В функцию так же могут передаваться аргументы (параметры), хотя это и не обязательно, переданные аргумент можно использовать исключительно внутри функции (об областях видимости переменных расскажу позднее). Допустим, что у нас есть некая функция SomeFunction, вот пример ее вызова без аргуменов:

SomeFunction()

и с аргументами one и two, они разделяются запятыми:

SomeFunction(one, two)

Теперь о нашем коде, надеюсь теперь видно что у нас имеется вызов функции print, имеющей один аргумент, которым является «Hello world!». Функция print — стандартная (базовая) функция, предоставленная луа, она выводит данные в стандартное окно вывода, эта функция может принимать любое число аргументов любого типа, выводя их друг за другом в преобразованном в строку виде.

А сейчас настало время поговорить о типах переменных, это один из значимых моментов в программирвании на луа. В этом языке существуют следующие типы данных (этот и некоторый далее материал заимствован с сайта ilovelua.narod.ru):

nil — ничего, обозначает отсутствие какого либо значения
boolean — булевская переменная, принимает значения true либо false
number числовой тип
string — строковый тип
function — функция
userdata — специальный тип данных, позволяющий хранить в Lua данные из С (фактически это указатель void*)
thread — сопрограмма Lua (позволяет организовать превдо-многопоточность)
table — таблица — ассоциативный массив (набор пар ключ-значение), причем в качестве и ключа и значения может выступать любой тип данных

Lua — язык с динамической типизацией, то есть тип переменной устанавливается не заранее, как, например, в С, а в момент присвоения переменной значения. Примеры:

var = true — var — переменная типа boolean
var = 1 — теперь var число
var = "string" — теперь var строка
var = function (a, b) return a + b end — а теперь var функция, которая принимает два параметра и возвращает их сумму
var = coroutine.create (var) — а теперь var сопрограмма
var = { } — а тепеь var таблица
var = nil — а теперь… теперь var отсутствует, перменаая уничтожена

Два символа — означают начало однострочного комментария, в котором может записываться что угодно.

А теперь после этой маленькой справки ответьте мне к какому типу относится «Hello world!»? Правильно, это строка, если вы думаете иначе — читаем все заново. Вот и весь разбор полетов, вышло что в нашем простом скрипте имеется один вызов стандартной функции луа что выводит в консоль любые переданные ей параметры, преобразовывая их в строку, мы передаем фукнции один аргумент, являющийся строкой «Hello world!», которую мы и видим в консоли после запуска скрипта.

Мой вам совет: если не понятен смысл речевых конструкций лучше не пропускать этот момент и постараться понять что написано в этой простенькой справке, иначе можно упустить важные моменты и в самом простом моменте в скрипте сесть в лужу.

Немного порадовавшись переходим к как мне кажется важной справке, следует принять её как должное. В луа мы можем объявлять переменные, которые могут являться любым из типов луа, но так же есть зарезервированные имена переменных, ими пользоваться нельзя, это приведет к нарушению работы вашего скрипта! Вот полное перечисление:

and break do else elseif
end false for function if
in local nil not or
repeat return then true until while

Так называть переменные нельзя т.к. эти слова используются в конструкциях языка луа. Давайте договоримся дальше что писать названия переменных мы будем в соответствии с венгерской нотацией, то есть называть переменные в соответствии с их типом, начиная переменную с маленькой буквы, обозначающей тип переменной, а уже дальше писать само имя нашей переменной:

s — string
i — integer
b — boolean
h — handle
t — table
mt — metatable

Примеры: sData, iShareSize, bConnected, hFile, tTable, mtTable. Примечанице: здесь под handle подразумевается тип userdata.

Теперь справка по спец. символам в строках луа, вот цитата из лексических соглашений:

Литеральные строки должны быть заключены в одинарные или двойные кавычки и могут содержать следующие С-подобные escape-поледовательности: ‘\a’ («звонок»), ‘\b’ («забой»), ‘\f’ («перевод страницы»), ‘\n’ («перевод на новую строку»), ‘\r’ («возврат каретки»), ‘\t’ («горизонтальная табуляция»), ‘\v’ («вертикальная табуляция»), ‘\\\»‘ («двойная кавычка»), and’\» (апостроф [«одинарная кавычка»]). Кроме того, обратный слеш ставится перед концом строки в редакторе, когда для удобства набора длинные непрерывные строки записываются в несколько строк. Символ в строке также может быть представлен своим кодом с помощью escape-последовательности \ddd, где ddd- последовательность из не более чем трех цифр. (Заметим, что если после символа, записанного с помощью своего кода, должна идти цифра, то код символа в escape-последовательности должен содержать ровно три цифры). Строки в Lua могут содержать любые 8-битные значения, включая ноль, который записывается как ‘\0’.

Что реально полезно:

\t — табуляция, применяется для более красивого вывода информации
\\ — так экранируется сам слеш чтобы вывести его как \, в ином случае будет ошибка
\n — перенос строки
\r — возврат каретки
\» — двойная кавычка
\’ — одинарная кавычка

Для переноса строки на разных операционных системах используется разное сочетание символов переноса строки и возврата каретки:

\r\n — windows
\n — *nix
\r — macOS

Пока все, думаю это вам еще надо обдумать, правда на практике все намного понятнее, попробуйте сами сделать выводы через функцию print() и поиграть с типами, кстати, функция type() возвращает тип переданной ей переменной, написав print(type(«»)) мы получим string