Русский остров - Krievusala
Сайт помощи новичкам сетей DC++
Суббота, 20.07.2019, 04:03
Реклама от Google
Меню сайта
Категории раздела
Настройка клиентов DC++ [4]
В Латвии [3]
Разные статьи о латвийских проблемах.
Владельцам хабов [4]
Клиенты DC++ [10]
Информация о популярных клиентах DC++
Разное [4]
Статьи на разные темы.
Полезный софт [1]
Описание различных, малоизвестных программ.
YouTube
Мини-чат
200
Статистика
Счетчик тИЦ, PR и обратных ссылок

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Поиск
Главная » Статьи » Владельцам хабов

Про Lua (часть 1)
   

Про Lua


Введение
Типы данных
Область видимости переменных
Функции
    Замороженные переменные
    Некоторые особенности при вызове функций
    Переменное число параметров
Таблицы
    Массивы 
    Обход элементов таблицы
    Методы
    Метатаблицы
    ООП
    Модули
        Загрузка модулей
    ООП на модулях



Введение

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


Типы данных

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!

Два символа -- означают начало одно строчного комментария.


Область видимости переменных

Переменные могут быть локальными или глобальными. Локальные переменные объявляются с ключевым словом local и определены внутри блока. При выходе за пределы блока значение локальной переменной становится nil. Блоком является либо файл, либо последовательность выражений между ключевыми словами do и end.

Пример:

local x = 10 -- локальная переменная х


Переменная становится глобальной после первого присвоения ей значения.

Пример:


y = 20 -- глобальня переменная y


Функции

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

Пример:

function outer()
    function inner()
        return 1
    end
     return inner
end

local i  = outer() -- значение переменной i равно функции inner
local j = i() -- вызываем функцию i, значение переменной j равно 1

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

Пример:

function f(x, y, z) -- определение функции
end

f() -- вызов функции. x, y, z равны nil
f(1) -- вызов функции. x = 1, y, z равны nil
f(1, 2) -- вызов функции. x = 1, y = 2, z равно nil
f(1, 2, 3) -- вызов функции. x = 1, y = 2, z = 3
f(1, 2, 3, 4) -- вызов функции. x = 1, y = 2, z = 3, 4 лишний параметр

Функции могут возвращать несколько значений.

Пример:

function f()
    return 1, 2, 3 -- функция возвращает три числа
end

local x, y, z = f() -- вызов функции. x = 1, y = 2, z = 3

Переменная _ используется в Lua для замены ненужной переменной.

Например, из функции f() нам нужно получить только третий параметр:

local _, _, z = f() -- z = 3


"Замороженные" переменные (upvalues)

Функция может обращаться к локальным переменным, которые определены вне её. Если при вызове функции локальная переменная уже вышла из области видимости и была разрушена, то функция пользуется тем значением внешней локальной переменной (external local variable), которое было на момент определения функции.

Пример:

local x = 10 -- установили значение x
function f()
    return x + 1 -- на момент создания функции значение переменной x равно 10
end
x = 20 -- изменили x
local y = f() -- значение у равно 21, локальная переменная х продолжает существовать.

Изменим время жизни локальной переменной x:

do -- начало блока
    local x = 10 -- установили значение x
    function f()
        return x + 1 -- на момент создания функции значение переменной x равно 10
    end
end -- конец блока, переменной x больше нет
local y = f() -- значение у равно 11, хотя переменная х больше не существует

Функции, кстати, тоже могут быть локальными:

local f = function(x, y, z) -- да, функцию можно объявить и так, это ведь всего лишь один из типов данных.
    return z, y, x -- хе-хе, неплохая функция swap получилась, верно?
end


 Некоторые особенности при вызове функций

Например, есть две функции:

function f1()
    return 1, 2
end

function f2(x, y)
    return x + y
end


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

loсal x = f2( f1() ) -- значение x равно 3

Задачка посложнее:

function f1()
    return 1, 2, 3
end

function f2(x, y, z)
    return x + y + z
end

local x = f2(f1()) -- x = 1 + 2 + 3

Теперь так:

local x = f2(4, f1()) -- x = 4 + 1 + 2

Понятно, что f1 дополняет последние параметры f2 нужным числом своих параметров. Однако это работает, только если вызов f1 стоит последним в списке параметров.

Что будет если вызвать функции так:

local x = f2(f1(), 4, 5) -- x = 1 + 4 + 5

В этом случае из f1 будет взят только первый параметр.

А если так:

local x = f2(f1(), 4) -- x = 1 + 4 + nil - ошибка!

Да, третьим параметром будет nil, что при выполнении математической операции сгенерирует ошибку.


Переменное число параметров

В Lua, как и в С, в функцию можно передавать переменное число параметров.

Синтаксис также похож:

function f(x, y, ...)
end


Все параметры, скрытые за ... Lua упаковывает в локальную таблицу arg. У таблицы arg есть поле n, содержащее число переданных аргументов.

Для примера, напечатаем все переданные в функцию аргументы:

function f(...)
    for i = 1, arg.n do
        print(arg[i])
    end
end
                      
f(1, 2, 3, "Вася") -- выведет соответственно 1, 2, 3, "Вася"


Таблицы

Таблица - это набор пар ключ-значение. Ключом в таблице может быть любое значение, кроме nil. Значением  поля в таблице так же может быть любое значение, кроме nil.

Cоздание таблицы:

local t = {}

Заполним ее различными типами данных:

t[1] = 10
t["Вася"] = "осел"
t[3] = true

local function f()
    return 1
end

t[f] = "функция"
t["функция"] = f
t.me = t -- тоже самое, что t["me"] = t - ссылка на самого себя
print(t["функция"]()) -- > 1

Таблица может быть про инициализирована при создании:

local t = {1, 2, 3, x = 5, ["Вася"] = "осел"}


Массивы

Если при инициализации таблицы ключи не были указаны, то Lua сама присваивает значениям ключи, начиная с 1.

Внимание! Индексация массивов в Lua начинается с 1, не с 0!

local t = {3, 4, 5}
print(t[1], t[2], t[3]) -- > 3, 4, 5

Важный момент:

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

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

local t = {1, 2, 3, 4, 5}
table.insert(t, 6) -- добавляет элемент в конец массива. Теперь  t = {1, 2, 3, 4, 5, 6}
table.insert(t, 0, 1) --вставляет элемент по индексу, сдвигая оставшиеся элементы массива. Теперь  t = {0, 1, 2, 3, 4, 5, 6}
table.remove(t, 3) -- удаляет из таблицы элемент по индексу 3 и сдвигает оставшиеся элементы. Теперь t = {0, 1, 3, 4, 5, 6}

Получение размера массива выполняется оператором #:

local count = #t

Оператор # возвращает целое число n, такое, что t[n] не nil, и t[n + 1] равно nil. Другими словами оператор #, возвращает  максимальный индекс непрерывной последовательности ключей от начала массива.

Соответственно, для таблицы:

local t = {1, [100] = 2}
print(#t) -- > 1, поскольку t[1] не nil, а t[1 + 1] равно nil.

Для массива, в котором значения хранятся одно за другим, оператор # вернет количество элементов в массиве.


Обход элементов таблицы

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

Массив обходится обычным циклом
for:

local t = {1, 2, 3, "Вася"}
for i = 1, #t, 1 do
    print(t[i])
end


-- > 1
-- > 2
-- > 3
-- > Вася

Обычные таблицы обходятся циклом for с итератором:

local t = {1, 2, x = 4, y = 5, ["Вася"] = "осел"}
for key, value in pairs(t) do
    print(key, value)
end

-- > 1    1
-- > 2    2
-- > y    5
-- > x    4
-- > Вася    осел

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


Методы

Рассмотрим следующую конструкцию (попытка с имитировать ООП):

local t = {x = 1}

function t:fun()
    print(self.x)
end
                     
t:fun()


-- > 1

 Что мы здесь сделали?


1. Создали таблицу t;
2. В таблице t завели поле x и назначили ему значение 1;
3. В таблице t завели функцию fun;
4. Внутри функции fun обратились к таблице через специальную переменную self (на самом деле self это первый скрытый параметр у функции fun. Вызывая функцию как t:fun() , мы на самом деле сделали то же самое, что и t.fun(t))
5. Вызвали у таблицы (уже почти объекта!) t метод fun;

На что все это похоже? Это похоже на ООП. Есть, объект, есть метод объекта, есть вызов метода объекта. Чего нет? Нет возможности создавать объекты определенного типа (например типа t). То есть и объект, и метод существуют в единственном экземпляре. Для того, чтобы создать объект типа tt, который ведет себя так же, как объект типа t, нам придется повторить всю процедуру с самого начала.

Пример:

local tt = {x = 2}

function tt:fun()
    print(self.x)
end
                     
function checkMetod(x)
    x:fun()
end

checkMetod(t)
checkMetod(tt)


--> 1
--> 2

Есть ли выход из сложившейся ситуации? Да, есть. Но о нем мы поговорим после того, как рассмотрим метатаблицы.


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

Метатаблицы позволяют переопределить поведение встроенных типов. Для таблиц это можно сделать прямо в Lua, а для других типов придется это делать через С.
Метатаблицы схожи с конструкцией operator() в C++.

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

-- первый операнд
local data1 = {x = 1}
-- второй операнд
local data2 = {x = 2}
-- создаем таблицу
local meta = {}
-- в таблице определяем специальное поле __add (оператор +)
function meta.__add(op1, op2)
  return op1.x + op2.x
end

-- устанавливаем метатаблицу первому операнду
setmetatable(data1, meta)
-- проверяем
print(data1 + data2) --> 3

Итак, что мы сделали? Создали два объекта, каждый из которых содержит поле х. Затем создали обычную таблицу meta. В ней определили функцию со специальным именем __add. Затем установили метатаблицу первому операнду.
С тем же успехом мы могли бы установить эту метатаблицу и второму операнду. Когда Lua обнаруживает операцию над значением, то сначала ищется подходящий обработчик в метатаблице первого операнда, и если там нет, то ищется обработчик в метатаблице второго операнда. Если обработчика и там нет, то генерируется ошибка.
Вот список операций, обработчики которых могут быть переопределены в метатаблице:


Обработчик Оператор Lua Примечание
__add(op1, op2) + сложение
__sub(op1, op2) - вычитание
__mul(op1, op2) * умножение
__div(op1, op2) / деление
__mod(op1, op2) % деление по модулю
__pow(op1, op2) ^ возведение в степень
__unm(op) - унарный минус
__concat(op1, op2) .. конкатенация (склейка)
__len(op) # унарный оператор взятия длины
__eq(op1, op2) == оператор "равно"
__lt(op1, op2) < оператор "меньше"
__le(op1, op2) <= оператор "меньше либо равно"
__index(op, key) [] оператор обращения по ключу
вызовется если, например,  вызвать local x = data1[1]
__newindex(op, key, value) [] оператор присвоения нового значения по ключу
если сделать эту функцию пустой, то таблица станет "readonly", то есть в нее нельзя будет добавить новое поле со значением
 вызовется если, например,  вызвать data1[1] = 1
__call(op, ...) () оператор вызова функции
вызовется если, например,  вызвать data1(1, 2, 3)
в обработчик __call первым параметром придет операнд (в нашем случае data1), а следующими параметрами будут те параметры, что стояли в скобках (в нашем случае 1, 2, 3).

Как видно, все очень просто.

В начало

Категория: Владельцам хабов | Добавил: Leonkrevs (13.12.2010) | Автор: Неизвестен!
Просмотров: 2111 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Driver.ru - Один из самых крупнейших в интернете, архив драйверов


  • Если Ваш компьютер заблокировали и требуют для разблокировки отправить СМС, то Вам сюда
  • Удаление баннера с рабочего стола, разблокировка Windows
  •  



    Яндекс.Погода

    Получи свой бонус


    Обмен WebMoney
      Отдадите:  
      Получите:  


    Вверх

    © //leonkrevs.ucoz.ru,,2019