Гайд | Lua для чайников {2 часть}

Гайды
  • Универсальный гайд по входу в кодинг на Lua

    гайд полностью написан мной (t1m1yep) при помощи своих знаний и документации Lua для octothorp team и его сообщества 🙂
    обязательно изучи прошлую часть, перед изучением второй!

    Предисловие

    еще раз привет! меня зовут тимофей и во второй части Луа для чайников мы изучим:

    • таблицы
    • строки (продвинуто)
    • замыкания
    • конструкторы
    • функции (продвинуто)
    • библиотеки и require
    • работа с ошибками (error handling)
    • корутины
    • условия (продвинуто)
    • и закрепим предыдущий материал!

    таблицы

    таблица представляет из себя “массив”, который вместо индекса имеет ключ. рассмотрим на примере:

    local mytable = {} -- инициализируем пустую таблицу
    local key = "taiwan" -- создаем переменную-строку "ключ"
    mytable[key] = "roobys" - задаем значение обьекту таблицы с ключом который мы создали ранее
    print(mytable[key]) -- выводим значение обьекта таблицы  с ключом key!
    

    этот код выведет строку “roobys”. давайте немного изменим этот код, в частности, последнюю строку:

    local mytable = {} -- инициализируем пустую таблицу
    local key = "taiwan" -- создаем переменную-строку "ключ"
    mytable[key] = "roobys" - задаем значение обьекту таблицы с ключом который мы создали ранее
    print(mytable) -- выводим таблицу без ключа! саму переменную
    

    выведется что то по типу “table: 0x158e09d80”
    что это?!
    это адрес таблицы в памяти компьютера. ее местоположение, ячейка, которую она занимает в ОЗУ

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

    local mytable = {["skumbri"]="скумбри", ["р12"]="r12", ["zverok99"]="зверек99"}
    

    в этом случае мы заранее инициализировали обьекты нашей таблицы и ключи к ним. mytable с ключом “skumbri” хранит значение “скумбри”!

    также мы можем создать таблицу-матрешку:

    local octothorp_team = {
        managers = {["inquizzy"]="инквиззи", ["vondik"]="вондик"}, 
        admins = {["skumbri"]="скумбри", ["campot"]="кампот"} 
    }
    print(octothorp_team.managers["inquizzy"])
    print(octothorp_team.admins["campot"])
    

    только в этом случае нам надо будет обращаться к под-таблицам через точку “.”

    примечание: ключи могут быть как и строками, так и числами и даже boolean’ами
    примечание: ключи в таблицах указываются в квадратных скобках, а после, через равно идет значение

    пример итерации по обьектам таблицы:

    local octothorp_team = {["doctor"]="доктор",["kilo7"]="кило-семь", ["polkin"]="полкин"}
    for key, value in pairs(octothorp_team) do 
        print(key, value)
    end
    

    в этом случае я использовал функцию pairs()
    этот код выведет:

    polkin  полкин
    kilo7   кило-семь
    doctor  доктор
    

    замыкания

    замыкания - “мини функции”
    рассмотрим на примере:

    local myfunction = function (a, b)
        return a + b
    end
    
    print(myfunction(10, 10))
    

    для удобства и чистоты кода - сделаем обьявление функции-замыкания однострочным

    local myfunction = function (a, b) return a + b end
    
    print(myfunction(10, 10))
    

    получается, что мы задаем нашей переменной myfunction значение функции. уже забыл?! функция - это тоже тип данных! я говорил об этом в первой части.

    мы можем вызывать эту переменную точно также как и функцию. рассмотрим еще один пример:

    function create_closure() 
        return function(a, b) return a + b end
    end
    
    local closure_function = create_closure()
    
    print(closure_function(1, 2))
    

    closure - замыкание по английски.
    мы создаем функцию, которая возвращает функцию-замыкание, а эта функция-замыкания суммирует два числа. получается, что функции точно также могут возвращать функции-замыкания с помощью return. прямо как обычные числа и строки!

    строки

    был бы это си… ой, что-то я замечался, продолжим!
    строки имеют свою собственную библиотеку! как бы это банально не звучало, звать ее - string!
    рассмотрим функцию gsub библиотеки string. она может заменить часть строки!

    local a = "доброград плохой сервер" -- создаем строку
    local b = string.gsub(a, "плохой", "крутой")  -- меняем часть строки и записываем в другую переменную
    print(a)       -- неправильная строка
    print(b)       -- правильная строка
    

    **выводом будет: **

    доброград плохой сервер
    доброград крутой сервер
    

    аргументы функции gsub() - первоначальная строка, с которой будут проводится манипуляции; слово которое должно быть изменено; слово НА которое надо изменить
    функция gsub() возвращает обновленную строку

    операции с задним слэшем (форматирование текста)

    задний слэш - “” в составе строки может иметь после себя определенную букву. каждая строка заканчивается на “\0” - nil символ, он обозначает конец строки. ты его даже не замечаешь! но запомни это, ведь любой инструмент может тебе пригодится (даже старая мышь, вдруг новая сломается!)

    \n - знак новой строки. все что будет после него - с новой строки
    \t - табуляция
    \r - очистить всю консоль! (не ставь это в конце, иначе ничего не увидишь 🙂 лучше ставь в начале!)
    также имеется большое количество таких операций, их ты можешь найти в интернете.

    ТВОЙ РОДНОЙ ЯЗЫК ДЖАВАСКРИПТ ЧТО ЛИ С**А?!

    наверняка многие из вас видели этот мем
    iQvA3tY.png
    это происходит из за сложения строк

    local яблоко = "яблоко"
    local одно = "одно"
    local одно_яблоко = одно + яблоко
    

    да, баян, но я считаю что стоит осветить эту тему 🙂
    также мы получим ошибку если попытаемся провести операцию с двумя разными типами:

    local chelog = "Дмитрий Антонов"
    local pi = 3.14
    local pichelog = chelog + pi -- выдаст ошибку, мы не можем сложить Дмитрия Антонова и 3.14
    print(pichelog) 
    

    DvFNZk3.png

    мы можем сложить яблоко с яблоком, но банан с яблоком не можем.

    таинственный кодер

    конструкторы

    ты до этого применял конструкторы, с таблицами и массивами! да-да, те самые фигурные скобки при создании таблиц и массивов - конструкторы. давай посмотрим пример:

    local массив_админов = {"r12", "skumbri", "campot", "timetocoffee", "what"} 
    local таблица_админов = {["рубус"]="roobys", ["эр12"]="r12", ["скумбре"]="skumbri", ["кампот"]="campot", ["кофейня"]="timetocoffee"} 
    print(массив_админов[3]) -- выведет timetocoffee
    print(таблица_админов["эр12"]) -- выведет r12
    

    Библиотеки, require

    чтобы создавать чистый и красивый код в больших проектах, скорее всего, тебе придется прибегать к разделению кода по разным категориям

    представим такую ситуацию: ты вывел в отдельный файл одну очень сложную функцию (нет) которая возвращает строку “Дмитрий Антонов” и теперь хочешь использовать эту функцию и библиотеку в основном файле. тут тебе поможет require:

    файл chelog_library.lua

    function dmitry_antonov() 
        return "Дмитрий Антонов"
    end
    

    файл main.lua

    require "chelog_library"
    
    local dima = dmitry_antonov()
    print(dima)
    

    почему ты написал только название файла в require? где расширение .lua??

    если вы импортируете локальный файл, которого нет папке всех библиотек lua, указывайте только название файла, иначе компьютер будет искать его в корне библиотек и он ее просто не найдет!

    также вы можете импортировать из папки:
    Pssxokl.png
    в таком случае понадобится указать дочернюю папку в main.lua:

    require "libs/chelog_library"
    
    local dima = dmitry_antonov()
    print(dima)
    

    в файле библиотеки ничего менять не нужно.

    но что если main.lua тоже находится в папке?
    muiEjMU.png
    в таком случае нам надо задать package.path перед require:

    package.path = package.path .. ";../libs/chelog_library.lua"
    require("chelog_library")
    
    local dima = dmitry_antonov()
    print(dima)
    

    Yvzka9Z.png
    а если библиотека находится вне папки libs, а просто папкой выше main.lua, мы можем убрать из package.path путь в папку “/libs”:

    package.path = package.path .. ";../chelog_library.lua"
    require("chelog_library")
    
    local dima = dmitry_antonov()
    print(dima)
    

    условия (продвинутые)

    рассмотрим несколько условий:

    булевы значения в if
    local lovechelog = true
    if lovechelog then 
        print("i love chelog") 
    elseif not lovechelog then
        print("deleting system...")
    end
    

    выведется “i love chelog”, а если заменить lovechelog на false, выведется “deleting system…”

    ключевое слово “not” буквально обозначает “не”

    в случае с булевым значениями не обязательно указывать сравнение по типу: “if boolean == true …”, можно просто написать “if boolean …”, это будет означать одно и тоже. а если надо указать “if boolean == false …”, можно написать “if not boolean”, это означает тоже самое.

    elseif входит в состав конструкции if, так как end у них общий. elseif используется только в случае повторения условия с участием одной и той же переменной.

    “||” и “&&”

    || - “или”

    if 10 > 5 || 20 > 21 then -- выполнится только первая часть условия, но само условие выполнится
         print("octothorp team") -- надпись выведется
    end
    
    if 10 < 5 || 20 < 21 then -- выполнится только вторая часть условия, но само условие выполнится
         print("octothorp team") -- надпись выведется
    end
    

    && - “также”

    if 10 > 5 && 20 > 10 then -- условие выполнится
       print("octothorp team") -- надпись выведется
    end
    
    if 10 > 5 && 20 < 10 then -- условие не выполнится, так как вторая часть не выполнена
       print("octothorp team") -- надпись НЕ выведется
    end
    

    работа с ошибками

    каждый разработчик имеет дело с ошибками и удерживает их (нервно пытается)

    error()

    функция error() принимает строку, которую будет печатать в консоль

    error("ошибка!")
    

    выведет:
    nPcNrWI.png

    assert

    давайте напишем простенькую программу, которая будет выдавать ошибку если мы будем пытаться прибавить число к строке:

    local dima = "dima"
    local num1 = 10
    local numdima = assert(type(num1) == "string", "num1 не должно быть числом") -- assert() вернет false и выключит программу
    print(numdima) -- мы никогда не узнаем...
    print(dima, num1) -- мы никогда не узнаем...
    

    piyYZqb.png
    поменяем переменную num1 на строку:

    local dima = "dima"
    local num1 = "antonov"
    local numdima = assert(type(num1) == "string", "num1 не должно быть числом") -- assert() вернет true
    print(numdima) -- выведет true
    print(dima, num1) -- выведет "dima antonov"
    

    DBL6z1Q.png

    корутины

    корутина - асинхронный блок кода (по очереди), мы можем в любой момент остановить ее выполнение, создать другую. они позволяют нам выполнять много кода одновременно, не замедляя выполнение одного другим

    создадим корутину:

    корутина = coroutine.create(function ()
        print("привет!") -- не выведется, так как горутина запускается в другом потоке!
    end)
    print(корутина) -- выведет thread: <адрес потока в котором запускается горутина>
    print(coroutine.status(co)) -- выведет suspended, корутина приостановлена
    coroutine.resume(co) -- выведет тот самый "привет!" в консоль, однако пожертвует своей жизнью (я не шучу)
    print(coroutine.status(co)) -- выведет dead... корутина умерла..
    

    немного исправим код и добавим coroutine.yield():

    корутина = coroutine.create(function () -- создаем корутину
        for i=1,3 do -- создаем цикл, он запустится три раза
            print("корутина с числом", i) -- печатаем номер итерации цикла
            coroutine.yield() -- функция yield() делает статус suspended, тоесть приостановлена
          end
    end)
    coroutine.resume(корутина) -- теперь мы возобновим коротину. три раза. выведет число 1
    print(coroutine.status(корутина)) -- статус suspended, так как после каждой итерации корутина приостанавливает себя и ждет возобновления
    coroutine.resume(корутина) -- выведет число 2
    print(coroutine.status(корутина)) -- suspended!
    coroutine.resume(корутина) -- выведет число 3
    print(coroutine.status(корутина)) -- suspended!
    

    если мы попытаемся возобновить коротину через coroutine.resume() которая уже мертва, мы получим ошибку

    мы также можем приостанавливать и возобновлять корутину с аргументами:

    корутина = coroutine.create(function (a,b) -- коротина-замыкание с двумя аргументами
        coroutine.yield(a + b, a - b) -- приостанавливаем корутину с аргументами для возобновления
      end)
    print(coroutine.resume(корутина, 20, 10)) -- даем те самые аргументы из yield(). выведет 30
    

    также коротина может возвращать значения как обычная функция:

    корутина = coroutine.create(function ()
        return 6, 7 -- возвращаем числа 6 и 7
      end)
    print(coroutine.resume(корутина))  -- выведет "6 7"
    

    Заключение второй части гайда

    спасибо что дочитал до этого момента! мне правда приятно что я смогу научить кого-то своим навыкам.

    глаза сильно болят и я уже хочу спать, 11 вечера по моему времени я сейчас лопну 🙂 (если что, я встал в 6)

    третяя часть

    обратная связь (вопросы насчет кода)

    смело пиши мне в личные сообщения дискорда - timofeyw
    я постараюсь ответить в кратчайшие сроки

    обратная связь (вопросы насчет топика, ошибки топика)

    можешь писать прямо в комментарии этого топика или мне в личные сообщения дискорда - timofeyw

    спасибо и удачи! третяя часть выйдет как можно скорее

  • @тимиеп Пиздануться можно, но прикольно!

  • тимиепТ тимиеп делает ссылку на эту тему

Похожие темы