learnxinyminutes-docs/ru-ru/lua-ru.html.markdown
2014-11-16 11:27:29 +03:00

17 KiB
Raw Blame History

language filename contributors translators lang
lua learnlua-ru.lua
Tyler Neylon
http://tylerneylon.com/
Max Solomonov
https://vk.com/solomonovmaksim
Max Truhonin
https://vk.com/maximmax42
ru-ru
-- Два дефиса начинают однострочный комментарий.

--[[
    Добавление двух квадратных скобок
    делает комментарий многострочным.
--]]
--------------------------------------------------------------------------------
-- 1. Переменные, циклы и условия.
--------------------------------------------------------------------------------

num = 42  -- Все числа являются типом double.
--[[ 
    Не волнуйся, 64-битные double имеют 52 бита
    для хранения именно целочисленных значений;
    точность не является проблемой для
    целочисленных значений, занимающих меньше
    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('больше 40')
elseif s ~= 'walternate' then  -- ~= обозначает "не равно".
  -- Проверка равенства это == как в Python; работает для строк.
  io.write('не больше 40\n')  -- По умолчанию стандартный вывод.
else
  -- По умолчанию переменные являются глобальными.
  thisIsGlobal = 5  -- Стиль CamelСase является общим.

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

  -- Для конкатенации строк используется оператор .. :
  print('Зима пришла, ' .. line)
end

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

aBoolValue = false

-- Только значения nil и false являются ложными; 0 и '' являются истинными!
if not aBoolValue then print('это значение ложно') 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('путь будущего')
  num = num - 1
until num == 0

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

function fib(n)
  if n < 2 then return n 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 = function(x) return math.sin(x) end
-- Эквивалентно для local function g(x)..., кроме ссылки на g в теле функции
-- не будет работать как ожидалось.
local g; g  = function (x) return math.sin(x) end
-- 'local g' будет прототипом функции.

-- Так же тригонометрические функции работсют с радианами.

-- Вызов функции с одним текстовым параметром не требует круглых скобок:
print 'hello'  -- Работает без ошибок.

-- Вызов функции с одним табличным параметром так же не требуют круглых скобок (про таблицы в след.части):
print {} -- Тоже сработает.

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

-- Таблицы = структура данных, свойственная только для Lua; это ассоциативные массивы.
-- Похоже на массивы в PHP или объекты в JS
-- Так же может использоваться как список.


-- Использование словарей:

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

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

-- Литеральная нотация для любого (не пустой) значения ключа:
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])  -- Начинается с ОДНОГО!
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)
  local 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 значения так же могут быть function(tbl, key) для настроенного
-- просмотра.

-- Значения типа __index,add, ... называются метаметодами.
-- Полный список. Здесь таблицы с метаметодами.

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

--------------------------------------------------------------------------------
-- 3.2 Class-like tables and inheritance.
--------------------------------------------------------------------------------

-- Classes aren't built in; there are different ways to make them using
-- tables and metatables.

-- Explanation for this example is below it.

Dog = {}                                   -- 1.

function Dog:new()                         -- 2.
  local 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 acts like a class; it's really a table.
-- 2. "function tablename:fn(...)" is the same as
--    "function tablename.fn(self, ...)", The : just adds a first arg called
--    self. Read 7 & 8 below for how self gets its value.
-- 3. newObj will be an instance of class Dog.
-- 4. "self" is the class being instantiated. Often self = Dog, but inheritance
--    can change it. newObj gets self's functions when we set both newObj's
--    metatable and self's __index to self.
-- 5. Reminder: setmetatable returns its first arg.
-- 6. The : works as in 2, but this time we expect self to be an instance
--    instead of a class.
-- 7. Same as Dog.new(Dog), so self = Dog in new().
-- 8. Same as mrDog.makeSound(mrDog); self = mrDog.

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

-- Inheritance example:

LoudDog = Dog:new()                           -- 1.

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

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

--------------------------------------------------------------------------------
-- 1. LoudDog gets Dog's methods and variables.
-- 2. self has a 'sound' key from new(), see 3.
-- 3. Same as "LoudDog.new(LoudDog)", and converted to "Dog.new(LoudDog)" as
--    LoudDog has no 'new' key, but does have "__index = Dog" on its metatable.
--    Result: seymour's metatable is LoudDog, and "LoudDog.__index = Dog". So
--    seymour.key will equal seymour.key, LoudDog.key, Dog.key, whichever
--    table is the first with the given key.
-- 4. The 'makeSound' key is found in LoudDog; this is the same as
--    "LoudDog.makeSound(seymour)".

-- If needed, a subclass's new() is like the base's:
function LoudDog:new()
  local newObj = {}
  -- set up newObj
  self.__index = self
  return setmetatable(newObj, self)
end

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


--[[ I'm commenting out this section so the rest of this script remains
--   runnable.
-- Suppose the file mod.lua looks like this:
local M = {}

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

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

return M

-- Another file can use mod.lua's functionality:
local mod = require('mod')  -- Run the file mod.lua.

-- require is the standard way to include modules.
-- require acts like:     (if not cached; see below)
local mod = (function ()
  <contents of mod.lua>
end)()
-- It's like mod.lua is a function body, so that locals inside mod.lua are
-- invisible outside it.

-- This works because mod here = M in mod.lua:
mod.sayHello()  -- Says hello to Hrunkner.

-- This is wrong; sayMyName only exists in mod.lua:
mod.sayMyName()  -- error

-- require's return values are cached so a file is run at most once, even when
-- require'd many times.

-- Suppose mod2.lua contains "print('Hi!')".
local a = require('mod2')  -- Prints Hi!
local b = require('mod2')  -- Doesn't print; a=b.

-- dofile is like require without caching:
dofile('mod2')  --> Hi!
dofile('mod2')  --> Hi! (runs again, unlike require)

-- loadfile loads a lua file but doesn't run it yet.
f = loadfile('mod2')  -- Calling f() runs mod2.lua.

-- loadstring is loadfile for strings.
g = loadstring('print(343)')  -- Returns a function.
g()  -- Prints out 343; nothing printed before now.

--]]

References

I was excited to learn Lua so I could make games with the Love 2D game engine. That's the why.

I started with BlackBulletIV's Lua for programmers. Next I read the official Programming in Lua book. That's the how.

It might be helpful to check out the Lua short reference on lua-users.org.

The main topics not covered are standard libraries:

By the way, the entire file is valid Lua; save it as learn.lua and run it with "lua learn.lua" !

This was first written for tylerneylon.com, and is also available as a github gist. Have fun with Lua!