Lua (язык программирования)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 19:32, 1 июня 2016.
Lua
Lua-logo.png
Парадигма мультипарадигмальный: императивный, функциональный, прототипно-ориентированный, скриптовый, встраиваемый
Спроектировано Роберту Иерузалимски, Валдемар Селиш, Луиш Энрике ди Фигейреду
Первый   появившийся 1993
Печать дисциплины динамическая, строгая, "утиная"
Расширение файла .lua
Главная реализация
Lua, LuaJIT, LLVM-Lua, LuaCLR, Nua, Lua Alchemy
Диалект
MetaLua
Под влиянием
Scheme, Снобол, Модула, Клу, C++
Влияние
Io, GameMonkey, Squirrel, Dao, MiniD

Lua представляет собой быстрый и легковстраваемый скриптовый язык программирования. Он сочетает в себе простой процедурный синтаксис с гибкими конструкциями описания данных, основанных на ассоциативных массивах и расширяемой семантике. Lua является динамически типизированным и интерпретируемым. Интерпретатор переводит исходный код в байт-код для регистровой виртуальной машины и является свободно-распространяемым, с открытыми исходными кодами на языке Си. В стандартной виртуальной машине Lua используется распределение памяти со сборкой мусора (аналогично Java или .NET).

По идеологии, возможностям и реализации язык ближе всего к JavaScript, но Lua имеет более мощные и гораздо более гибкие конструкции. Механизмы объектно-ориентированного программирования, включая множественное наследование, могут быть реализованы с использованием метатаблиц, но понятие классов не содержится в Lua. Реализуемая объектная модель может быть названа прототипной (как в JavaScript). Использование языка в основном происходит для создания тиражируемого программного обеспечения. По причине легкого встраивания, скорости работы и легкости обучения язык получил признание среди пользователей, которым требуется язык программирования уровней и расширений во многих играх. Lua является свободным программным обеспечением с открытым исходным кодом, распространяется под лицензией MIT.

История создания

Lua разработана подразделением Tecgraf (группа технологий компьютерной графики) Католического университета Рио-де-Жанейро в Бразилии. Язык используется с 1993 года. Авторы языка — Роберту Иерузалимски, Луиш Энрике ди Фигейреду (Luiz Henrique de Figueiredo) и Валдемар Селиш (Waldemar Celes).

Родителями Lua были языки конфигурирования и описания данных SOL (Simple Object Language) и DEL (Data-Entry Language). Они были независимо разработаны в Tecgraf в 1992—1993 годах для добавления некоторой гибкости в два отдельных проекта. Из-за недостатков в SOL и DEL(отсутствовали управляющие конструкции), и Petrobras чувствовал необходимость в добавлении к ним полноценного программирования.

Lua 1.0 была спроектирована так, что её конструкторы объектов включали в себя синтаксис языка SOL (отсюда название Lua: по-португальски sol — «солнце», lua — «луна»). Управляющие конструкции Lua были взяты из Modula (if, while, repeat/until), CLU (параллельное присваивание, множественное возвращаемое значение функции как более простая альтернатива вместо передачи параметров по ссылке или явных указателей), C++, SNOBOL и AWK (ассоциативные массивы).

Версии Lua вплоть до 5.0 выпускались под лицензией, подобной лицензии BSD. Начиная с версии 5.0 и выше Lua распространяется под лицензией MIT. Обе лицензии являются пермиссивными и практически идентичны.

Инструменты

Автономный интерпретатор (также называемый lua.c по имени файла исходного кода, или просто lua по исполняемому файлу) представляет собой небольшую программу, позволяющую прямое использование Lua. Когда интерпретатор загружает файл, он игнорирует первую строку, если она начинается со "знака числа" ("#"). Эта особенность позволяет использовать Lua как интерпретатор скриптов в Unix-системах. Если вы предваряете свой код строкой вида

    #!/usr/local/bin/lua

(при условии, что автономный интерпретатор расположен в /usr/local/bin), или

    #!/usr/bin/env lua

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

Запуск lua:

    lua [options] [script [args]]

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

Например,

    prompt> lua -e "print(math.sin(12))"   --> -0.53657291800043

(В системе Unix необходимо задавать двойные кавычки для разделения команд интерпретатора от собственно исполняемого кода.) Как было сказано выше, ключ -l загружает файл, а -i переводит интерпретатор в интерактивный режим после обработки других ключей (опций). Так, например, вызов

    prompt> lua -i -l a.lua -e "x = 10"

загрузит файл a.lua, затем выполнит присваивание x = 10, и, наконец, предоставит командную строку для ввода.

Скрипты на языке помечаются расширением .lua. Для задач, критичных по времени, имеется JIT-компилятор Lua — LuaJIT. Также разработан компилятор llvm-lua, генерирующий код для виртуальной машины LLVM, предоставляющей возможность последующей компиляции в очень эффективный машинный код для процессоров различной архитектуры.

Основные принципы

Лексические соглашения

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

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

Lua является языком, чувствительным к регистру символов: and – ключевое слово, тогда как And и AND– два разных допустимых идентификатора. По соглашению, имена, начинающиеся с символа подчеркивания и записанные в верхнем регистре (например _VERSION), зарезервированы для использования в качестве внутренних глобальных переменных, используемых Lua. Литеральные строки должны быть заключены в одинарные или двойные кавычки и могут содержать С-подобные escape-поледовательности.

Типы и переменные

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

  1. Nil - это тип значения nil [пустое значение]. Его главное свойство – отличаться от всех остальных значений и обозначать отсутствие пригодного значения.
  2. К типу Boolean относятся значения false (ложь) и true (истина).Значения nil и false считаются ложными, любое другое значение считается истинным. К типу Number относятся вещественные числа (двойной точности с плавающей запятой).
  3. String обозначает массивы символов.Строки Lua могут содержать любые 8 битные символы, включая ноль ('\0').
  4. Тип userdata (пользовательские данные) позволяет хранить любые данных из C в переменных Lua.Значение этого типа является ссылкой на блок физической памяти и не имеет предопределенных операций в Lua, за исключением присваивания и проверки на равенство. Однако, используя метатаблицы, програмист может определить операции над значениями этого типа.
  5. Тип thread (поток) обозначает независимый поток исполнения и используется при реализации механизма сопрограмм. Нельзя отождествлять потоки Lua с потоками операционной системы. Lua поддерживает подпрограммы даже в тех системах, где потоки на уровне операционной системы не поддерживаются.
  6. Тип table (таблица) определяет ассоциативные массивы.Такие массивы могут индексироваться не только числами, но и любыми значениями (за исключением nil). Таблица может содержать значения сразу нескольких типов (кроме nil). Таблицы представляют собой единственный механизм структурирования данных в Lua; они могут использоваться как простые массивы, таблицы символов, множества, поля записей, деревья и так далее. Для представления словарей Lua использует имя поля в качестве индекса таблицы. Представление в виде a.name считается тождественным представлению a["name"]. Так как функции являются значениями встроенного типа, поля таблицы могут содержать и функции. Таким образом, таблицы могут хранить методы methods.

Переменные типа table, function, thread и userdata не содержат самих данных, в них хранятся только ссылки на соответствующий объект.

В Lua есть три вида переменных: глобальные, локальные и поля таблиц. Любая переменная считается глобальной, если она явно не объявлена как локальная. Локальные переменные существуют в лексическом контексте: локальные переменные доступны функциям, определенным внутри этого контекста.

local x, y, z
local a, b, c = 1, 2, 3
local x = x

Операторы

В Lua поддерживается в общем стандартный набор операторов, почти как в Pascal или C. Он состоит из операторов присваивания, операторов управления потоком исполнения, вызова функций и описания переменных.

Ниже перечислены основные операции:

  -            смена знака
  + - * /      арифметика
  ^            возведение в степень

  == ~=        равенство

  < <= > >=    порядок

  not and or   логика

  ..           конкатенация строк
  #            получение длины строки или массива

При сравнении на равенство не производится преобразование типов. Объекты разных типов всегда считаются различными. При вычислении значения используется короткая схема — второй аргумент вычисляется только если это необходимо. Действует следующая таблица приоритетов и ассоциативности операций:

 [right]    ^
 [left]     not   #     -(unary)
 [left]     *     /
 [left]     +     -
 [left]     <     >     <=     >=     ~=     ==
 [right]    ..
 [left]     and
 [left]     or

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

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

Операторы можно (но не обязательно) разделять символом ';'.

Ниже перечислены основные операторы:

  do ... end

  if ... then ... end
  if ... then ... else ... end
  if ... then ... elseif ... then ... end
  if ... then ... elseif ... then ... else ... end

  while ... do ... end
  repeat ... until ...

  for var = start, stop do ... end
  for var = start, stop, step do ... end

  return
  return ...
  break

Блок do ... end превращает последовательность операторов в один оператор и открывает новую область видимости, в которой можно определять локальные переменные.

В операторах if, while и repeat все значения выражения, отличные от false и nil трактуются как истинные. Оператор return может не содержать возвращаемых значений или содержать одно или несколько выражений (список). Операторы return и break должны быть последними операторами в блоке (т.е. должны быть либо последними операторами в блоке кода, либо располагаться непосредственно перед словами end, else, elseif, until).

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

  do
    local var, _limit, _step = tonumber(start), tonumber(stop), tonumber(step)
    if not (var and _limit and _step) then error() end
    while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do
      ...
      var = var + _step
    end
  end

Функции

Определение функции — это исполняемое выражение (конструктор функции), результатом вычисления которого является объект типа функция:

  f = function( ... ) ... end

В скобках размещается список (возможно пустой) аргументов функции. Список аргументов функции может заканчиваться троеточием — в этом случае функция имеет переменное число аргументов. Между закрывающейся скобкой и оператором end размещается тело функции.

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

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

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

  function rename( arg )
    arg.new = arg.new or arg.old .. ".bak"
    return os.rename(arg.old, arg.new)
  end

  rename{ old = "asd.qwe" }

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

Каждая таблица и объект типа userdata могут иметь мета-таблицу — обычную таблицу, поля которой определяют поведение исходного объекта при применении к нему некоторых специальных операций. Например, когда объект оказывается операндом при сложении, интерпретатор ищет в мета-таблице поле с именем __add и, если такое поле присутствует, то использует его значение как функцию, выполняющую сложение. Индексы (имена полей) в мета-таблице называются событиями, а соответствующие значения (обработчики событий) — метаметодами.

По умолчанию вновь созданная таблица не имеет мета-таблицы. Любую таблицу mt можно сделать мета-таблицей таблицы t, вызвав функцию setmetatable(t, mt). Функция getmetatable(t) возвращает мета-таблицу таблицы t или nil, если таблица не имеет мета-таблицы.

Lua определяет следующие события:

  __add, __sub, __mul, __div      арифметические операции
  __pow                           возведение в степень
  __unm                           унарный минус
  __concat                        конкатенация
  __eq, __lt, __le                операции сравнения
  __index                         доступ по отсутствующему индексу
  __newindex                      присвоение новому элементу таблицы
  __call                          вызов функции
  __tostring                      преобразование в строку
  __metatable                     получения мета-таблицы
Примеры использования мета-таблиц

В следующем примере мета-таблицы используются для назначения значения по умолчанию для отсутствующих элементов таблицы:

  function set_def(t, v)
    local mt = { __index = function() return v end }
    setmetatable(t, mt)
  end

Вот более нетривиальное решение, которое не использует отдельной мета-таблицы для каждого умалчиваемого значения:

  local key = {}
  local mt = { __index = function(t) return t[key] end }

  function set_def(t, v)
    t[key] = v
    setmetatable(t, mt)
  end

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

Пакеты

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

  my_package = {}
      
  function my_package.foo()
    ...
  end

Также можно делать все функции локальными и отдельно формировать таблицу экспортируемых функций:

  local function foo() ... end
  local function bar() ... end

  my_package = {
    foo = foo,
    bar = bar,
  }

Пакет загружается с помощью функции require(), причем во время загрузки имя, переданное этой функции (оно может не содержать расширения, которое добавляется автоматически) доступно через переменную _REQUIREDNAME:

  if _REQUIREDNAME == nil then
    run_some_internal_tests()
  end

Классы и объекты

Конструкция tbl:func() (при объявлении функции и при ее вызове) предоставляет основные возможности, позволяющие работать с таблицей как с объектом. Основная проблема состоит в порождении многих объектов, имеющих сходное поведение т.е. порожденных от одного класса:

  function class()
    cl = {}
    cl.__index = cl            -- cl будет использоваться как мета-таблица
    return cl
  end

  function object( cl, obj )
    obj = obj or {}            -- возможно уже есть заполненные поля
    setmetatable(obj, cl)
    return obj
  end

Здесь функция class создает пустую таблицу, подготовленную к тому, чтобы стать мета-таблицей объекта. Методы класса делаются полями этой таблицы т.е. класс является таблицей, одновременно содержащей методы объекта и его мета-методы.

Функция object() создает объект заданного класса — таблицу, у которой в качестве мета-таблицы установлен заданный класс. Во втором аргументе может быть передана таблица, содержащая проинициализированные поля объекта.

  Some_Class = class()

  function Some_Class:foo() ... end

  function Some_Class:new()
    return object(self, { xxx = 12 })
  end

  x = Some_Class:new()

  x:foo()

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

Стандартные Lua библтотеки содержат часто используемые функции, которые выполняются непосредственно в C API. Некоторые из этих функций предоставляют важные сервисы языка (например, type и getmetatable); другие обеспечивают доступ к "внешним" сервисам (например, ввод/вывод); частично они реализованы на Lua, однако часто используемые и имеющие критическое время выполнения, реализованы на C (например, sort).

Все библиотеки вызываются через официальный C API и выполняются как отдельные C модули. В настоящий момент, Lua имеет следующие стандартные библиотеки:

  • базовая библиотека (basic library);
  • библиотека пакетов (package library);
  • работа со строками (string manipulation);
  • работа с таблицами (table manipulation);
  • математические функции (sin, log, и т.п.);
  • ввод/вывод;
  • работа с операционной системой (operating system facilities);
  • отладка (debug facilities).

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

Список литературы

  1. Официальный сайт разработчиков Lua
  2. Сайт пользователей Lua
  3. Серия статей о Lua
  4. Справочное руководство по Lua версии 5.3