kstep 28.02.2011 17:13

CodingВведение в Lua для программистов — часть 2.3

Немного раньше я показал работу с функциями. Теперь я расскажу о типах userdata и thread, а так же затрону тему области видимости переменных.



Франшиза

Тип данных userdata стоит несколько особняком. По сути это аватар произвольной C-структуры в мире Lua. Или, если угодно, своеобразная франшиза, бренд, под которым в Lua видны кастомные типы данных. Строго говоря нет такого одного отдельного типа данных — userdata, есть множество различных разнородных кастомных типов данных, которые создаются либо в C-библиотеках, либо в хост-приложении, которое пользует Lua (не забываем, что Lua очень часто используется не как самостоятельный интерпретатор, а как библиотека, прилинкованная к «большому» приложению, и вот это приложение может создать произвольное окружение для выполняемых в его рамках Lua-скриптов; оно и будет хостом для Lua-программ). В Lua все такие кастомные типы (обычно это C-шные struct-ы, но это необязательно) видны как один тип — userdata, хотя и природа данных под этим типом может быть совершенно разная.

Примером типа userdata может, например, послужить объект screen в Awesome:

1
2
3
4
5
$ awesome-client
awesome# return screen<1>
string "userdata: 0x9eb54d0"
awesome# return type(screen<1>)
string "userdata"



В данном случае хост-приложение Awesome передаёт в Lua-окружение специальную таблицу-массив screen, элементы которых содержать объекты, представляющие физический экран. Хотя внешне эти элементы видны как объекты типа userdata, фактически это объекты типа screen, предоставляющие интерфейс к физическому экрану: его разрешение, рабочую область за вычетом зон, занятых виджетами самого WM и порядковый номер экрана. Точно так же Awesome определяет несколько других типов данных в Lua для манипуляции с запущенными приложениями, изображениями и т.д. Подробнее можно про этот зоопарк почитать в официальной документации.

Таким образом userdata является собирательным образом всех кастомных типов, которые для Lua предоставляет C-часть, поэтому описывать его практически невозможно: нужно брать конкретную реализацию, библиотеку, смотреть конкретные типы данных и читать про их интерфейс, методы и свойства, которые они определяют. Создать такой кастомный тип средствами самого Lua невозможно, да, как правило, и не нужно.

Клубок нитей

Lua поддерживает исполнение кооперативных потоков, для этого есть специальная библиотека coroutine. Если честно я сомневаюсь, стоит ли рассказывать сейчас про все её фичи, потому что сопрограммы и мультритрединг (в любом виде, в том числе и кооперативный) — это тема отдельной большой статьи, которую я ещё напишу. Поэтому здесь я просто упомяну, что такая библиотека есть, а тем, кому не в терпёж, (и позволяет теоретическая и практическая подготовка) могут прочитать про неё в документации к Lua прямо сейчас. Если кратко, то объекты типа thread создаются функциями из библиотеки coroutine, представляют один поток выполнения и предоставляют интерфейс для манипуляции с этим потоком.

Переменные под прикрытием

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

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- создаём переменную b
b = 10

-- здесь изменяется переменная b
function setb()
b = 20
end

-- здесь изменяется переменная a
function seta()
a = 30
end

-- выведет nil и 10
print(a, b)
seta()
setb()
-- выведет 30 и 20
print(a, b)



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

Локальные переменные объявляются с помощью ключевого слова local, закрывая по необходимости глобальные переменные с теми же именами:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function seta()
-- здесь a пока что глобальная
print("before local", a)
a = 20

-- а здесь она локальная
local a
print("after local", a)
a = 30
print("after local assignment", a)
end

-- глобальная a
a = 10

-- выведет 10
print(a)

--<< выведет:
before local 10
after local nil
after local assignment 30
>>
seta()

-- выведет 20
print(a)



Оператор local действует в пределах текущего блока операторов, при этом действие его начинается в момент определения переменной с этим ключевым словом (как видно в примере функция seta() могла модифицировать глобальную переменную до тех пор, пока она не была перекрыта локальной, объявленной оператором local).

Кроме того можно совместить присваивание переменной значения с её объявлением через local.

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

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
somevar = 10

-- локальная переменная в if-е:
if true then
local somevar = 20
print(somevar) -- выведет 20
end

-- локальная переменная в for:
for i = 1, 10 do
local somevar = i + 10
-- выведет числа от 11 до 20
print(somevar)
end

-- локальная переменная в while-е:
while true do
local somevar = 30
print(somevar) -- выведет 30

break -- превентивный выход из цикла
end

-- выведет 10
print(somevar)



Если хочется локальной переменной только в некотором куске кода, и не хочется объявлять циклов и функций, то можно воспользоваться универсальными операторными скобками do ... end:

 1
2
3
4
5
6
7
8
9
10
othervar = 100

do
local othervar = 50
-- будет 50
print(othervar)
end

-- будет 100
print(othervar)



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

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function outer()
-- то же, что и
-- local inner; inner = function() end
local function inner()
end
print(inner)

function inner2()
end
end

-- выведет что-то вроде function: 0x934da48
-- (функция inner)
outer()

-- функция inner не попадёт в глобальную область видимости:
-- выведет nil
print(inner)

-- но функция inner2 будет видна снаружи:
-- выведет что-то вроде function: 0x934d610
print(inner2)



Локальные переменные имеют лексическую область видимости:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
a = 10
function geta()
print(a)
end

function seta()
local a = 20
geta()
end

-- выведет 10, потому что локальная a из seta()
-- лексически заперта в функции seta(), так что geta()
-- увидит только глобальную a.
seta()



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

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

Итог: мы наскоро познакомились с двумя последними типами данных — userdata и thread, узнали про локальные переменные (которые объявляются оператором local и имеют лексическую область видимости) и универсальный операторный блок do ... end. Кроме того внимательных читателей ожидал бонус в виде оператора break для досрочного выхода из цикла.

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


Тэги: lua luaintro программирование
+ 16 -
Похожие Поделиться

neqste 28.02.2011 19:59 #
Очень часто сталкиваюсь с данным языком программирования в играх. Пример тому ragnarok online. спасибо за интересную статью
ascrazy 01.03.2011 00:02 #
Может стоит сделать что нить типа глобальной навигации по всем статьям серии?
vvorth 01.03.2011 08:24 #
Да, было бы хорошо в каждую статью включить ссылки на все предыдущие, а можно даже обновлять ранние, добавляя в них ссылки на все следующие =.)
kstep 01.03.2011 14:20 #
Дык, вот же.
GalS 02.03.2011 11:26 #
мультритрединг
многопоточность?
в if-е:
...
в for:
...
в while-е:
нет однообразности