пятница, 15 августа 2014 г.

Шпаргалка по Lua

-- с двойного тире начинается однострочный комментарий

--[[
    А если добавить двойные квадратные скобки
    получится многострочный комментарий
--]]
----------------------------------------------------
-- 1. Переменные и управляющие конструкции
----------------------------------------------------

num = 42  -- Все числа в Lua представляют из себя числа с плавающей точкой двойной точности (double)
-- Не переживайте, у 64-битных double имеется 52 бита
-- для хранения точных целочисленных (int) значений;
-- для целочисленных значений, которым нужно менее 52 бит, никаких проблем не возникнет

s = 'walternate'  --строки неизменны (нельзя переназначать) как в Рython'e
t = "double-quotes are also fine" -- можно использовать и двойные кавычки
u = [[ Double brackets         
             start and end           
             multi-line strings.]]   -- Двойные квадратные скобки 
                                     -- обрамляют многострочные строки (strings)
t = nil  -- обнуляет t; В Lua имеется "сборщик мусора"

-- Блоки обозначаются ключевыми словами такими как do/end:
while num < 50 do
    num = num + 1  -- в Lua нет операторов ++ или -- или +=
end

-- Блоки условия
if num > 40 then
    print('over 40')
elseif s ~= 'walternate' then  -- ~= обозначает неравенство
    -- равенство проверяется оператором == как в Python; 
    -- используется и для строк
    io.write('not over 40\n')  -- Стандартный вывод
else
    -- Переменные "глобальны" изначально
    thisIsGlobal = 5  -- без комментариев

    -- Как создать локальную переменную
    local line = io.read()  -- Считывает следующую строку

    -- Для соединения строк используется оператор .. (две точки)
    print('Winter is coming, ' .. line)
end

-- Обнулённые переменные возвращают nil.
-- Это не будет ошибкой:
foo = anUnknownVariable  -- Теперь foo = nil.

aBoolValue = false

-- Только типы nil и false возвращают "false";
-- 0 и '' (пустая строка) возвращают "true"!
if not aBoolValue then print('twas false') end

-- Следующее выражение подобно конструкции a?b:c в C/js:
ans = aBoolValue and 'yes' or 'no'  --> 'no'

karlSum = 0
for i = 1, 100 do  -- Диапазон включает оба конца (1 и 100)
    karlSum = karlSum + i
end

-- Используйте "100, 1, -1", чтобы считать с конца
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end

-- В целом схема такова:  for "начальное значение", "конечное значение","шаг"

-- Цикл с постусловием:
repeat
    print('the way of the future')
    num = num - 1
until num == 0

----------------------------------------------------
-- 2. Функции
----------------------------------------------------

function fib(n)
    if n < 2 then return 1 end
    return fib(n - 2) + fib(n - 1)
end

-- закрытые и безымянные функции тоже допустимы
function adder(x)
    -- Возвращаемая функция создаётся когда вызывается adder 
    -- и запоминает значение x:
    return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16))  --> 25
print(a2(64))  --> 100

-- Функции можно возвращать, вызывать и передавать
-- со списками, которые могут не совпадать по длине
-- Несовпадающие полученные параметры обнуляются
-- Несовпадающие переданные - отбрасываются

x, y, z = 1, 2, 3, 4
-- Сейчас x = 1, y = 2, z = 3, а 4 отброшено

function bar(a, b, c)
    print(a, b, c)
    return 4, 8, 15, 16, 23, 42
end

x, y = bar('zaphod')  --> напечатает "zaphod  nil nil"
-- Теперь x = 4, y = 8, а значения 15..42 отброшены

-- Функции могут быть локальными и глобальными
-- Следующие выражения равнозначны:
function f(x) return x * x end
f = function (x) return x * x end

-- Также как и:
local function g(x) return math.sin(x) end
local g; g  = function (x) return math.sin(x) end

-- Кстати, тригонометрические функции работают с радианами

-- Вызывать однострочные параметры можно и без скобок
print 'hello'  -- Работает!

----------------------------------------------------
-- 3. Таблицы
----------------------------------------------------

-- Таблицы = составные данные структуры в Lua;
--           они являются ассоциативными массивами.
-- Подобно массивам в php или объектам в js ,
-- ищут по хеш-значениям и могут быть использованы как списки

-- хранение данных в таблице по типу "значение-ключ":

-- Переменные имеют строковые ключи по умолчанию
t = {key1 = 'value1', key2 = false}

-- Строковые ключи могут обозначаться с точкой как в js:
print(t.key1)  -- Напечатает 'value1'.
t.newKey = {}  -- Добавляет новую пару "значение-ключ"
t.key2 = nil   --Удаляет key2 из таблицы

-- объявление таблицы с указанием ключей и значений (кроме nil):
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28])  -- напечатает "tau"

-- Сопоставление ключей идёт, в основном,  по значениям строк и чисел
-- но позволительно использовать и таблицы.
a = u['@!#']  -- Теперь a = 'qbert'.
b = u[{}]     -- Мы ожидаем, что это будет 1729, но на самом деле это nil:
-- b = nil т.к. найти значение не удалось, из-за того, что 
-- ключ, который мы использовали не является тем же объектом,
-- что мы использовали для сохранения значения.
-- Отсюда вывод: лучше использовать в качестве ключей строки и числа.

-- Вызывать таблицу с одним параметром можно и без скобок:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'}  -- Напечатает 'Sonmi~451'.

for key, val in pairs(u) do  -- Перебор таблицы
    print(key, val)
end

-- _G это особая таблица для всех глобальных переменных
print(_G['_G'] == _G)  -- Выдаст 'true'.

-- Использование таблиц как списков/массивов:

-- Значениям списка неявно присваивается числовой ключ:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do  -- #v размер списка v
    print(v[i])  -- Индексирование начитается с 1 !! Безумие!
end
-- Вообще-то нет такого типа как список. v это просто таблица
-- с последовательными  числовыми ключами.

----------------------------------------------------
-- 3.1 Метатаблицы и метаметоды
----------------------------------------------------

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

f1 = {a = 1, b = 2}  -- Представляет деление a/b.
f2 = {a = 2, b = 3}

-- Вот так не получиться:
-- s = f1 + f2

metafraction = {}
function metafraction.__add(f1, f2)
    sum = {}
    sum.b = f1.b * f2.b
    sum.a = f1.a * f2.b + f2.a * f1.b
    return sum
end

setmetatable(f1, metafraction)
setmetatable(f2, metafraction)

s = f1 + f2  -- вызывает __add(f1, f2) в метатаблице f1

-- f1, f2 не имеют ключей в метатаблицах, в отличии от
-- прототипов в js, они находятся по функции
-- getmetatable(f1). Метатаблица - та же таблица с
-- специальными для Lua ключами, например __add.

-- Выполнить следующую строку не удастся,
-- так как s не имеет своей метатаблицы:
-- t = s + s
-- Создание подобия классов поможет это исправить:

defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- Работает! Спасибо метатаблице!

-- Если во время поиска не нашлось значение в таблице,
-- совершается повторный поиск в поле  __index метатаблицы

-- Значением __index может быть и функция function(tbl, key)

-- __index,add, .. называются метаметодами
-- Полный список смотрите ниже:

-- __add(a, b)                     for a + b
-- __sub(a, b)                     for a - b
-- __mul(a, b)                     for a * b
-- __div(a, b)                     for a / b
-- __mod(a, b)                     for a % b
-- __pow(a, b)                     for a ^ b
-- __unm(a)                        for -a
-- __concat(a, b)                  for a .. b
-- __len(a)                        for #a
-- __eq(a, b)                      for a == b
-- __lt(a, b)                      for a < b
-- __le(a, b)                      for a <= b
-- __index(a, b)  [fn or a table]  for a.b
-- __newindex(a, b, c)             for a.b = c
-- __call(a, ...)                  for a(...)

----------------------------------------------------
-- 3.2 Подобие классов и наследование
----------------------------------------------------

-- В Луа нет классов как таковых.
-- Но существуют разные способы их "создания"

-- Смотрети пример ниже:

Dog = {}                                -- 1.

function Dog:new()                      -- 2.
    newObj = {sound = 'woof'}           -- 3.
    self.__index = self                 -- 4.
    return setmetatable(newObj, self)   -- 5.
end

function Dog:makeSound()                -- 6.
    print('I say ' .. self.sound)
end

mrDog = Dog:new()                       -- 7.
mrDog:makeSound()  -- 'I say woof'      -- 8.

-- 1. Dog действует как класс; хотя на самом деле это таблица
-- 2. Запись функции    function tablename:fn(...)    равна 
--                      function tablename.fn(self, ...)
--    : (двоеточие) просто добавляет первым аргументом self
--    Ещё раз прочтите строки 7, 8, чтобы посмотреть как это работает
-- 3. newObj экземпляр класса Dog.
-- 4. self = экземпляр класса. Довольно часто
--    self = Dog, но наследование может это изменить
--    newObj получает функции self когда мы назначаем
--    метатаблицу newObj  self'у
-- 5. Напоминаем: setmetatable возвращает первый аргумент
-- 6. : (двоеточие) работает также как и во 2 строке, 
--    но на этот раз self - экземпляр, а не класс
-- 7. Также как Dog.new(Dog), отсюда self = Dog в new().
-- 8. Также как mrDog.makeSound(mrDog); self = mrDog.

----------------------------------------------------

-- Пример наследования:

LoudDog = Dog:new()                           -- 1.

function LoudDog:makeSound()
    s = self.sound .. ' '                     -- 2.
    print(s .. s .. s)
end

seymour = LoudDog:new()                       -- 3.
seymour:makeSound()  -- 'woof woof woof'      -- 4.

-- 1. LoudDog получает методы и переменные Dog
-- 2. self получает поле 'sound' из new(), смотри строку 3.
-- 3. Создаётся экземпляр LoudDog.
-- 4. Поле 'makeSound' находится LoudDog;
-- Тоже что и LoudDog.makeSound(seymour).

function LoudDog:new()
    newObj = {}
    -- назначаем newObj
    self.__index = self
    return setmetatable(newObj, self)
end

----------------------------------------------------
-- 4. Модули
----------------------------------------------------


--[[ Я просто закомметил эту часть статьи
--   но код в ней всё так же рабочий.

-- Предположим, файл mod.lua выглядит так:
local M = {}

local function sayMyName()
    print('Hrunkner')
end

function M.sayHello()
    print('Why hello there')
    sayMyName()
end

return M

-- Другой файл может использовать функционал mod.lua
local mod = require('mod')  -- Запускает mod.lua.

-- require стандартный метод подключения модулей
-- require работает так:     (if not cached; see below)
local mod = (function ()
    [contents of mod.lua]
end)()
-- Получается как mod.lua это тело функции, поэтому
-- локальные переменные в mod.lua доступны и за его пределами

-- Здесь переменная mod = M в файле mod.lua:
mod.sayHello()  -- Поздоровались с Hrunkner.

-- А вот это неверно; sayMyName существует только в mod.lua:
mod.sayMyName()  -- ошибка

-- Значения, возвращаемые require, кешированы
-- и запускаются только раз, даже если вы написали require несколько раз

-- Предположим,  mod2.lua содержит "print('Hi!')".
local a = require('mod2')  -- Напечатает Hi!
local b = require('mod2')  -- Не напечатает; a=b.

-- dofile подобен require но без кеширования:
dofile('mod2')  --> Hi!
dofile('mod2')  --> Hi! (может вызываться много раз, в отличии от require)

-- loadfile загружает файлы .lua но не запускает их
f = loadfile('mod2')  -- Вызвав f(), запуститься mod2.lua.

-- loadstring функция подобна loadfile только для строк
g = loadstring('print(343)')  -- Возвращает функцию
g()  -- выводит 343;

--]]

----------------------------------------------------
-- 5. Ссылки
----------------------------------------------------

--[[

Я начал учить Lua, потому что хотел
делать игры на Love 2D game engine. Вот так вот.

Начинал я со статьи "BlackBulletIV's Lua for programmers"
После прочитал официальную книгу "Programming in Lua"
Вот так и учил.

Так же может быть полезным прочитать коротенькую справку на lua-users.org.

В этой статье не освещена работа с основными библиотеками
 * string library
 * table library
 * math library
 * io library
 * os library

Кстати, статья представляет из себя рабочий .lua скрипт.
Можете сохранить и запустить.

Статья изначально писалась для сайта tylerneylon.com. 
(ссылка на оригинал статьи: http://tylerneylon.com/a/learn-lua/)
Вольный перевод на русский выполнен для сайта xgm.ru

Наслаждайтесь Lua!
Пару ссылок по Lua:
Простая но удобная IDE с отладчиком и доп. библиотеками - https://code.google.com/p/luaforwindows/
Краткая дока по языку - https://sites.google.com/site/ltwood/projects/lua/lua-lang
Мануал - http://www.lua.org/manual/5.1/manual.html

Комментариев нет:

Отправить комментарий