Изучаем Lua за 15 минут

Перевод: Al Rado - Twitter @alrado2, Никита Курылев Twitter @nikita_kurilev
Оригинал статьи на английском тут.


-- Два тире - начинают однострочный комментарий.

--[[
     Если добавить два знака "[" и "]", то получится 
     многострочный комментарий
--]]

----------------------------------------------------
-- 1. Переменные и управление процессами.
----------------------------------------------------

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

s = 'walternate'  -- Неизменные строки как в Python'е.
t = "Двойные кавычки тоже работают"
u = [[ Двойные квадратные скобки
       начинают и заканчивают
       многострочный текст.]]
t = nil  -- переменная t неопределена; У Lua присутствует сборщик мусора.

-- Блоки обозначаются с помощью "do" и "end":
while num < 50 do
  num = num + 1  -- Нет таких операторов как "++" или "+=".
end

-- Условия "Если":
if num > 40 then
  print('over 40')
elseif s ~= 'walternate' then  -- "~=" значит "не равно".
  -- Для проверки равенства используется "==" как в Python'e; подходит и для строк.
  io.write('not over 40\n')  -- По-умолчанию вывод на stdout.
else
  -- Переменные глобальны по умолчанию.
  thisIsGlobal = 5  -- Общепринят кэмелКейс.

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

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

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

aBoolValue = false

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

-- 'or' и 'and' используются для упрощения.
-- Это схоже с тернарным оператором "a?b:c" в C/js:
ans = aBoolValue and 'yes' or 'no'  --> 'no'

karlSum = 0
for i = 1, 100 do  -- В этот диапазон включены оба значения.
  karlSum = karlSum + i
end

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

-- В общем случае, для диапазона указывается начало, конец[, шаг].

-- Другая конструкция цикла:
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

-- Возвраты, вызовы функций, и назначения работают 
-- со списками, которые могут несовпадать по длине.
-- Несовпадающие получатели выставляются nil'ами;
-- несовпадающие отправители отбрасываются.

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
-- объявление 'local g' создает переменную, затем производится присвоение

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

-- Вызовы с одним строковым параметром не нуждаются в скобках:
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'}  -- Prints 'Sonmi~451'.

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

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

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

-- Список литералов неявно задает int-овые ключи: 
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
-- Классовые шаблоны, приведенные ниже, исправят это.

-- __index для метаданных перегружает точки просмотра:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal  -- работает! Благодаря метатаблице

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

-- Значение __index также может быть функцией (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 ниже о том, как 'self' получает свое значение.
-- 3. newObj будет экземпляром класса Dog.
-- 4. self = экземпляр класса. Часто
--    self = Dog, но наследование может изменить это.
--    NewObj получает функции self, когда мы устанавливаем оба значения:
--    метатаблицу newObj и __index 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 имеет «звуковой» ключ после применения new (), см. 3.
-- 3.То же, что LoudDog.new (LoudDog), и преобразуется в
-- Dog.new (LoudDog), поскольку LoudDog не имеет ключа 'new',
-- но у него есть __index = Dog в его метатаблице.
-- Результат:  метатаблица seymour это LoudDog, и
-- LoudDog.__ index = LoudDog. Так что seymour.key будет
-- равен seymour.key, LoudDog.key, Dog.key, какая 
-- таблица является первой с данным ключом
-- 4. Ключ 'makeSound' находится в LoudDog; 
-- это то же самое, что и LoudDog.makeSound (seymour)

-- При необходимости, создание подкласса может быть похоже на создание базового:
function LoudDog:new()
  newObj = {}
  -- set up 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 работает так: (если модули не кэшированы, см. Ниже)
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.lua')  --> Hi!
dofile('mod2.lua')  --> Hi! (runs it again)

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

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

--]]

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

--[[

Я был рад узнать Lua, чтобы я мог делать игры
на игровом движке Löve 2D. Вот почему.

Я начал с "BlackBulletIV Lua" для программистов.
Затем я прочитал официальную книгу "Программирование на языке Lua".
Вот как.

Может быть полезно пройти по  
ссылке на lua-users.org.

Основные темы, которые не были охвачены - стандартные библиотеки:
 * string library
 * table library
 * math library
 * io library
 * os library

Кстати, весь этот файл работоспособен на Lua; сохрани это
как learn.lua и запусти его [из командной строки] "lua learn.lua"!

Это было впервые написано для tylerneylon.com. Это
также доступно на github gist. Учебники для других
языков в том же стиле, что и этот, здесь:

http://learnxinyminutes.com/

Приятной работы с Луа!

--]]

results matching ""

    No results matching ""