---
title: "Начало работы с Lua - API7.ai"
description: "Lua — это легкий и высокоуровневый язык программирования, разработанный в первую очередь для встраивания в приложения. Начните изучать Lua прямо отсюда!"
url: "https://api7.ai/ru/learning-center/openresty/getting-started-with-lua"
---

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

API7.ai

September 23, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

После общего понимания основ NGINX мы продолжим изучение Lua. Это язык программирования, используемый в OpenResty, и необходимо освоить его базовый синтаксис.

Lua — это небольшой и изящный скриптовый язык, созданный в университетской лаборатории в Бразилии, чьё название означает "красивая луна" на португальском. NGINX был создан в России, Lua — в Бразилии, а OpenResty — в Китае, стране автора. Интересно, что эти три одинаково умные технологии с открытым исходным кодом появились в странах БРИКС, а не в Европе или Америке.

Lua был разработан как простой, лёгкий и встраиваемый язык-клей, который не стремится быть большим и мощным. Хотя вы, возможно, не будете писать код на Lua в своей повседневной работе, Lua широко используется. Многие онлайн-игры, такие как World of Warcraft, используют Lua для написания плагинов; Redis, база данных ключ-значение, имеет встроенный Lua для управления логикой.

С другой стороны, хотя библиотека Lua относительно проста, она легко может вызывать библиотеки на языке C, и многие зрелые коды на языке C могут быть использованы для неё. Например, в OpenResty вам часто нужно будет вызывать функции NGINX и OpenSSL на языке C, благодаря способности Lua и LuaJIT легко взаимодействовать с библиотеками C.

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

## Среда и hello world

Нам не нужно специально устанавливать стандартную среду Lua 5.1, потому что OpenResty больше не поддерживает стандартный Lua, только LuaJIT. Обратите внимание, что синтаксис Lua, который я представляю здесь, также совместим с LuaJIT и не основан на последней версии Lua 5.3.

Вы можете найти каталог и исполняемый файл LuaJIT в каталоге установки OpenResty. Я нахожусь в среде Mac и использовал brew для установки OpenResty, поэтому ваш локальный путь, вероятно, будет отличаться от следующего.

```
$ ll /usr/local/Cellar/openresty/1.13.6.2/luajit/bin/luajit
     lrwxr-xr-x  1 ming  admin    18B  4  2 14:54 /usr/local/Cellar/openresty/1.13.6.2/luajit/bin/luajit -> luajit-2.1.0-beta3
```

Вы также можете найти его в каталоге исполняемых файлов системы.

```
 $ which luajit
 /usr/local/bin/luajit
```

Проверьте версию LuaJIT.

```
$ luajit -v
 LuaJIT 2.1.0-beta2 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/
```

После проверки этой информации вы можете создать новый файл `1.lua` и использовать LuaJIT для запуска кода `hello world`.

```
$ cat 1.lua
print("hello world")
$ luajit 1.lua
 hello world
```

Конечно, вы также можете использовать `resty` для его запуска, зная, что он в конечном итоге выполняется с помощью LuaJIT.

```
$ resty -e 'print("hello world")'
 hello world
```

Оба способа запуска `hello world` возможны. Я предпочитаю подход с использованием `resty`, потому что много кода OpenResty также запускается с помощью `resty` позже.

## Типы данных

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

```
$ resty -e 'print(type("hello world"))
 print(type(print))
 print(type(true))
 print(type(360.0))
 print(type({}))
 print(type(nil))
 '
```

Будет выведена следующая информация.

```
 string
 function
 boolean
 number
 table
 nil
```

Это основные типы данных в Lua. Давайте кратко их представим.

### Строка

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

Давайте рассмотрим пример, чтобы проиллюстрировать этот недостаток. В Lua мы используем две точки для обозначения сложения строк. Следующий код объединяет числа от 1 до 10 в виде строк.

```
$ resty -e 'local s  = ""
 for i = 1, 10 do
     s = s .. tostring(i)
 end
 print(s)'
```

Здесь мы выполняем цикл десять раз, и только последний результат — это то, что нам нужно; девять новых строк между ними бесполезны. Они не только занимают дополнительное пространство, но и потребляют ненужные операции CPU.

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

Кроме того, в Lua у вас есть три способа выразить строку: одинарные кавычки, двойные кавычки и длинные скобки (`[[]]`). Первые два относительно легко понять и они обычно используются в других языках, так зачем нужны длинные скобки?

Давайте посмотрим на конкретный пример.

```
$ resty -e 'print([[string has \n and \r]])'
 string has \n and \r
```

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

Вы можете задать ещё один вопрос: что делать, если строка включает длинные скобки? Ответ прост: добавьте один или несколько символов `=` в середину длинных скобок.

```
$ resty -e 'print([=[ string has a [[]]. ]=])'
  string has a [[]].
```

### Логический тип

Это простой тип, `true` и `false`. В Lua только `nil` и `false` являются ложными; всё остальное — истина, включая 0 и пустую строку. Мы можем проверить это с помощью следующего кода.

```
$ resty -e 'local a = 0
 if a then
   print("true")
 end
 a = ""
 if a then
   print("true")
 end'
```

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

```
$ resty -e 'local a = 0
 if a == false then
   print("true")
 end
 '
```

### Число

Числовой тип в Lua реализован как число с плавающей точкой двойной точности. Стоит отметить, что LuaJIT поддерживает режим `dual-number`, что означает, что LuaJIT хранит целые числа как целые числа, а числа с плавающей точкой — как числа с плавающей точкой двойной точности, в зависимости от контекста.

Кроме того, LuaJIT поддерживает `long-long integers` для больших целых чисел, как в следующем примере.

```
$ resty -e 'print(9223372036854775807LL - 1)'
9223372036854775806LL
```

### Функция

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

Например, следующие два объявления функций абсолютно эквивалентны.

```
function foo()
 end
```

и

```
foo = function ()
 end
```

### Таблица

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

```
$ resty -e 'local color = {first = "red"}
print(color["first"])'
 red
```

### Пустое значение

В Lua пустое значение — это `nil`. Если вы определяете переменную, но не присваиваете ей значение, её значение по умолчанию — `nil`.

```
$ resty -e 'local a
 print(type(a))'
 nil
```

Когда вы войдёте в систему OpenResty, вы найдёте много пустых значений, таких как `ngx.null`, и так далее. Мы поговорим об этом позже.

Типы данных в Lua я в основном представил, чтобы дать вам основу. Мы продолжим изучать то, что вам нужно освоить позже в статье. Обучение через практику и использование всегда является наиболее удобным способом усвоения новых знаний.

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

Часто изучение языка — это действительно изучение его стандартных библиотек.

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

`API OpenResty > функции библиотеки LuaJIT > стандартные функции Lua` — это приоритет, который будет упоминаться неоднократно с точки зрения удобства использования и производительности.

Однако, несмотря на это, мы неизбежно будем использовать некоторые библиотеки Lua в наших реальных проектах. Здесь я выбрал несколько наиболее часто используемых стандартных библиотек, чтобы представить их, и если вы хотите узнать больше, вы можете ознакомиться с официальной документацией Lua.

### Библиотека строк

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

Одно простое правило: если задействованы регулярные выражения, обязательно используйте `ngx.re.*`, предоставляемый OpenResty, а не обработку `string.*` в Lua. Это связано с тем, что регулярные выражения в Lua уникальны и не соответствуют спецификации PCRE, и я уверен, что большинство инженеров не смогут с ними справиться.

Одна из наиболее часто используемых функций библиотеки строк — это `string.byte(s [, i [, j ]])`, которая возвращает ASCII-код, соответствующий символам `s[i], s[i + 1], s[i + 2], ------, s[j]`. Значение по умолчанию для `i` — 1, первый байт, а значение по умолчанию для `j` — `i`.

Давайте посмотрим на пример кода.

```
$ resty -e 'print(string.byte("abc", 1, 3))
 print(string.byte("abc", 3)) -- Отсутствует третий параметр, третий параметр по умолчанию равен второму, то есть 3
 print(string.byte("abc"))    -- Отсутствуют второй и третий параметры, оба по умолчанию равны 1
 '
```

Его вывод:

```
 979899
 99
 97
```

### Библиотека таблиц

В контексте OpenResty я не рекомендую использовать большинство библиотек таблиц, входящих в состав Lua, за исключением нескольких функций, таких как `table.concat` и `table.sort`. Что касается их деталей, мы оставим их для главы о LuaJIT.

Здесь я кратко упомяну `table.concat`. `table.concat` обычно используется в сценариях объединения строк, как в примере ниже. Это позволяет избежать создания множества бесполезных строк.

```
$ resty -e 'local a = {"A", "b", "C"}
 print(table.concat(a))'
```

### Математическая библиотека

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

В проектах OpenResty мы редко используем Lua для математических операций. Тем не менее, две функции, связанные с генерацией случайных чисел, `math.random()` и `math.randomseed()`, часто используются, как в следующем коде, который может генерировать два случайных числа в заданном диапазоне.

```
$ resty -e 'math.randomseed (os.time())
print(math.random())
print(math.random(100))'
```

## Фиктивные переменные

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

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

Не знаю, как вы к этому относитесь, но для меня, по крайней мере, пытаться дать осмысленные имена этим неиспользуемым переменным — это пытка.

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

Давайте возьмём стандартную библиотечную функцию `string.find` в качестве примера, чтобы увидеть использование фиктивных переменных. Эта стандартная библиотечная функция возвращает два значения, представляющие начальный и конечный индексы соответственно.

Если нам нужно только получить начальный индекс, достаточно объявить переменную для получения возвращаемого значения `string.find`, как показано ниже.

```
$ resty -e 'local start = string.find("hello", "he")
 print(start)'
 1
```

Но если вы хотите получить только конечный индекс, то вам нужно использовать фиктивную переменную.

```
$ resty -e 'local  _, end_pos = string.find("hello", "he")
 print(end_pos)'
 2
```

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

```
$ resty -e 'for _, v in ipairs({4,5,6}) do
     print(v)
 end'
 4
 5
 6
```

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

## Итог

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

Наконец, я хочу оставить вам ещё один вопрос для размышления.

Помните код, который вы изучили в этом посте, когда мы говорили о математической библиотеке? Он генерирует два случайных числа в заданном диапазоне.

```
$ resty -e 'math.randomseed (os.time())
print(math.random())
 print(math.random(100))'
```

Однако вы могли заметить, что код использует текущую метку времени в качестве начального значения. Есть ли проблема с этим подходом? И как мы должны генерировать хорошие начальные значения? Часто случайные числа, которые мы разрабатываем, не являются случайными и имеют серьёзные проблемы с безопасностью.

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

Share article link

Share

Related Posts

[![OpenResty FAQ | Как OpenResty используется на практике](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | Как OpenResty используется на практике](https://api7.ai/learning-center/openresty/faq-5)

[![Часть 3: Как создать API-шлюз для микросервисов с использованием OpenResty](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [Часть 3: Как создать API-шлюз для микросервисов с использованием OpenResty](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [Среда и hello world](#%D1%81%D1%80%D0%B5%D0%B4%D0%B0-%D0%B8-hello-world)

- [Типы данных](#%D1%82%D0%B8%D0%BF%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85)

  - [Строка](#%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B0)
  - [Логический тип](#%D0%BB%D0%BE%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9-%D1%82%D0%B8%D0%BF)
  - [Число](#%D1%87%D0%B8%D1%81%D0%BB%D0%BE)
  - [Функция](#%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F)
  - [Таблица](#%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0)
  - [Пустое значение](#%D0%BF%D1%83%D1%81%D1%82%D0%BE%D0%B5-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)

- [Стандартные библиотеки](#%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D0%B5-%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B8)

  - [Библиотека строк](#%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-%D1%81%D1%82%D1%80%D0%BE%D0%BA)
  - [Библиотека таблиц](#%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86)
  - [Математическая библиотека](#%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F-%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0)

- [Фиктивные переменные](#%D1%84%D0%B8%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5)

- [Итог](#%D0%B8%D1%82%D0%BE%D0%B3)
