Язык Lua и Corona SDK (2/3 часть)

    image

    Это вторая из трех частей статьи о базовых понятиях языка Lua для Corona SDK (если вы не читали первую часть советую начать с нее), в этой части мы рассмотрим все имеющие в языке типы данных. Вы узнаете, как инициализировать переменные всех типов, использовать их значения и преобразовывать один тип в другой. Эта статья является крайне важной в вопросе изучения языка и я постарался добавить большое количество ценных советов, которые позволят вам избежать множества ошибок когда вы будете писать свои игры.

    Типы данных и их преобразование


    В Lua имеется 8 типов данных. Перед рассмотрение типов остановлюсь на стандартной функции языка lua — type. Эта функция возвращает строку с названием типа данных указанной переменной или данных. Пример использования функции type:

    n = nil
    st = 'Вася'
    pi = 3.14
    func = function()end
    t = {}
    b = true
    co = coroutine.create(function ()end)
    print(type(n), type(st), type(pi), type(func), type(t), type(b), type(co))
    --результат: nil string number function table boolean thread

    Далее рассмотрим типы данный языка Lua.

    Тип nil


    Этот тип данных по умолчанию имеет любая переменная которой не назначено значение. Все переменные типа nil имеют значение nil. Переменной можно присвоить этот тип двумя способами:

    local B = nil--явно определяем тип
    local A--не явно определяем тип
    С--ошибка - для глобальных переменных нельзя не явно инициализировать nil

    Стоит заметить, что если переменная не была инициализирована в текущей области видимости ее тип и значение будут nil. Переменная имеющая тип nil может сравниваться в порядке сравнения логической переменной имеющей значение false через логическую операцию not, но в действительности эти типы не аналогичны, приведу пример:

    n = nil
    b = false
    if not n then--true
    	print(1)
    end
    if not b then--true
    	print(2)
    end
    if b == n then--false
    	print(3)
    end
    -->> 1
    -->> 2

    Тип boolean


    Тип boolean так же называют логическим или булевым типом, так как с помощью него производится реализация операций сравнения. Как уже выше писалось boolean имеет всего 2 возможных значения false и true (ложь и правда). Так как все операции сравнения возвращают именно эти значения — переменные этого типа можно инициализировать путем присвоения им значения сравнительной операции, так же можно инициализировать путем прямого назначения значения. Переменные типа boolean можно сравнивать только операциями равенства и неравенства, а так же непосредственно в качестве условия сравнения в if/elseif

    --прямая инициализация по значению
    b1 = false
    b2 = true 
    --инициализация через сравнение
    b3 = b1 ~= b2--true
    b4 = b1 == b2--false
    if b1 == b3 then--true
    	print(true)
    end
    if b3 then
    	print(true)
    end

    Тип number


    Расширенный численный тип, полное его описание значительно выходит за рамки этой текста, поэтому любознательные пользователи могу проследовать по ссылке и детально ознакомиться со спецификацией. Могу лишь сказать что вместить этот тип может как отрицательные так и положительные числа, как целые так и с плавающей запятой, при определенных уровнях положительной и отрицательной разрядности происходит автоматический переход в экспоненциальную форму чисел, пределы разрядности зависят от разрядности процессора устройства, но в текущий момент Corona SDK имеет поддержку Android 4.0.3+ что скорее всего означает что все имеющиеся устройства будут 64 разрядными. Что бы инициализировать переменные типа number достаточно либо явно присвоить значение в одном из доступных форматов либо присвоить переменной результат вычисления или приравнять ее другой переменной этого типа. Отмечу что любая переменная типа number в операциях логического сравнения возвращает true, даже 0 (и даже -1), это стоит учитывать любителям языка c++ (и подобных) где 0 — это аналог false. Приведу пример операций с типом number:

    local a,b  = 2,3--непосредственная инициализация 
    local c = a + b--присвоение результату вычисления
    
    --пример экспоненциальной записи
    d = 12e5 -- 1 200 000
    e = 2e-3 -- 0.002
    
    --пример шестнадцатеричной инициализации
    f = 0x234 -- 564
    --вещественные числа
    j = 1.23
    k = .55 -- 0.55 - для чисел меньше 1 ноль в начале не обязательно писать

    С переменными типа number можно проводить ряд численных операций, основные из которых показаны в примере:

    a = b + c -- сложение
    a = b - c --вычитание
    a = b * c -- умножение
    a = b / c -- деление
    a = b % c --получение остатка от деления
    a = b ^ c --возведение b в степень c

    Тип string


    Тип String весьма неплохо развит в Lua и в комплекте с переменными это типа идет множество удобств. Как создавать переменные типа string:

    local st1 = 'qwert'--присвоение строки
     st2 = st1--приравнивание другой переменной
    st3 = st1 .. st2--результат сложения двух строк

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

    i = 10--number
    st = '11'--string
    konc1 = 1 .. st -- конкатенация
    konc2 = 100..''--сцепление с пустой строкой переводит переменную в string
    print(konc1, konc2, type(konc1) ,  type(konc2 )-->>111  100  string string

    Как видите перевод из number в string достаточно простой. Хочу обратить внимание, что в случае если вы используете сцеплении строки лучше всего между оператором сцепления и операндами ставить пробел, это не всегда обязательно, но лучше привыкнуть так как во-первых так красивей и понятней, а во-вторых в случае если один из операндов — число (не переменная равная числу, а именно число), то если не поставить пробел возникнет ошибка "malformed number near". Есть и более традиционные подходы к конвертированию, приведу пример перевода в обе стороны:

    i1 = 1
    s1 = '11'
    i2 = tonumber(s1)--перевод string -> number
    s2 = tostring(i1)-- перевод number -> string

    Так же хочется добавить, что неявное конвертирование string в number то же существует, но категорически не советую вам так делать — это совершенно безобразно и почти наверняка когда-нибудь приведет к сложной ошибке:

    --пример того как не стоит делать
    st = '10'
    i = 10 + st
    print(i, type(i))-->> 20  number

    При инициализации переменных типа string не редко могут возникать сложные моменты, которые я кратко опишу и приведу способы их решения:

    • В строке имеется символ двойных или одинарных кавычек — это решается одним из двух способов: либо необходимо перед символом поставить слешь "\", это действие экранирует символ не давая ему завершать открытую кавычку, либо использовать другой вид кавычек, в Lua в качестве символа открытия и закрытия строки можно использовать как одинарные так и двойные кавычки.
    • В строке имеется символ слешь "\" это вызывает трудности как как интерпретатор считает что символ стоящий посте слеша — управляющий, что бы решить эту проблему необходимо ввести еще один слешь и строка "\\" будет считаться как будто символ 1

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

    string.len(s) / s:len()


    Функция позволяет определить длину строки. Эту как и большинство других функции из библиотеки string можно применять двумя способами, но имеются особенности, ниже приведу пример использования:

    --полная запись string.len(s)
    local st = 'Hello World!'
    local len_st = string.len(st)
    print(len_st)-->>12
    
    --сокращенная запись s:len()
    local le_st = st:len()
    print(len_st)-->>12
    
    --определение длины без переменной
    print(string.len('Examlpe string'))-->>14
    
    --так будет ошибка - нельзя использовать сокращенную 
    --запись для определения длины без переменной
    print('Examlpe string':len())-->> Error: ')' expected near ':'

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

    string.byte(s [, i [, j]]) / s:byte([i [, j]])


    Функция возвращает коды символов строки от i-го до j-того. Параметры i и j не являются обязательными и если не указать j будет возвращен только код i-того элемента, если не указать i, но указать j, будут возвращены коды символов с первого по j-тый, если не указать не i не j будет возвращен код первого символа в строке. Приведу пример использования функции:

    local s = 'Example'
    print(s:byte())--первый символ >> 69
    print(string.byte(s, 3))--третий символ >> 97
    print(string.byte(s,nil, 4))--с первого по 4-й >> 69  120  97  109
    print(s:byte(nil, s:len()))--все символы >> 69  120  97  109  112  108  101

    string.char(i1, i2, ...)


    Функции передается один или более параметров с номерами символов, возвращается строка равная всем переданным символам. Эта функция имеет обратное назначение относительно функции string.char(). Примеры использования:

    print(string.char(65)) -->> A
    print(string.char(72,101,108,108,111)) -->> Hello
    
    local s = 'Example'
    print(string.char(s:byte(1,s:len())))--извлекаем s:byte все символы 
    ---строки st и заново их собираем в исходную строку >> Example

    string.find(s, pattern [, index [, no_regular]])
    s:find(pattern [, index [,no_regular]])


    С помощью функции find можно найти в строке s позицию шаблона pattern или nil если не найдено. Шаблон pattern может быть как строкой так и регулярным выражением (подробно о регулярных выражениях вы узнаете в отдельной главе). Рассмотрим простейший вариант применения find далее перейдем к особенностям необязательных параметров:

    local s = "Hello Lua!"
    print(string.find(s,"Lua"))--ищем с какой по какую позицию 
    --находится строка Lua в строке s >> 7  9
    print(s:find("%sL"))--это регулярное выражение означает "Искать 
    --букву L перед которой имеется любой знак разделения слов" >> 6  7

    Параметр index по умолчанию равен 1 и это означает, что возвращается позиция первого найденного вхождения если искать с начала, если установить index равным к примеру 3, то будет возвращаться третье вхождение в строку начиная с начала, если же -3 то будет искаться 3 вхождение с конца строки. Параметр no_regular — логический, т.е. может принимать значения истина/ложь (true/false), по умолчанию параметр равен false, если же установить true то параметр pattern всегда будет восприниматься как обычная строка, даже если ввести в него регулярное выражение. Приведу пример расширенного применения find:

    s = 'La La La La %sLa La'
    print(s:find('La', 2)) --второе вхождение с начала >> 4  5
    print(s:find('La', -2)) --второе вхождение с конца >> 18  19
    print(s:find('%sLa', 1, true)) -->> игнорировать регулярку, искать как строку >> 13  16

    string.format(s, e1, e2, ...)
    s:format(e1, e2, ...)


    Функция string.format создает форматированную строку с набором аргументов. На первый взгляд эта функция может показаться сложной в понимании, но на самом деле она может сильно упрощать многие конструкции при выводе конечной информации, а так же весьма удобно применяется для других косвенных преобразований, например из number в строку шестнадцатеричного кода. Приведу простейший пример:

    local sum1,sum2 = 100,200-- выводимые значения
    print(string.format('Value1: %d, Value2: %d', sum1,sum2))-->> Value1: 100, Value2: 200

    Как видите, сначала идет формат строки, в котором содержатся аргументы показывающие каким образом нужно использовать параметры идущие следом за этим параметром. Существует 11 (c, d, E, e, f, g, i, o, u, X, x) аргументов предназначенных для управления числовыми значениями и 2(s,q) аргумента для управления строками. Далее кратко остановлюсь на каждом аргументе после чего приведу примеры использования:

    • %s — параметр считается строкой и просто вставляется
    • %q — параметр считается строкой и вставляется в двойных кавычках
    • %c — вместо числа будет подставлен символ номер которого в соответствующем параметре
    • %d — просто будет вставлено переданное число без изменения
    • %i — число представляется как целое число со знаком Signed типа integer
    • %u — число представляется как целое число без знака Unsigned типа integer
    • %o — число переводится в восьмеричную систему счисления
    • %x,%X — число переводится в шестнадцатеричную систему счисления, регистр букв в строке будет зависеть от регистра аргумента
    • %f,%g — число считается вещественным (с плавающей запятой), для %f формат полный, для %g укорочен до 5 знаков после запятой
    • %E,%e — число будет преобразовано в экспоненциальную форму, регистр буквы E в строке будет зависеть от регистра аргумента

    print(string.format('Number: %d, Signed: %i, Unsigned: %u', 100,-100,-200)) -->> Number: 100, Signed: -100, Unsigned: 4294967096
    print(string.format("%c%c%c%c%c%c%c", 69,120,97,109,112,108,101)) -->> Example
    print(string.format("%e, %E", math.pi,math.pi))-->> 3.141593e+000,  3.141593E+000
    print(string.format("%f, %g", math.pi,math.pi))-->> 3.141593,  3.14159
    print(string.format("%o, %x, %X", 1024,4069,16382)) -->> 2000, fe5, 3FFE
    print(string.format("%s %q", "Hello", "Corona SDK!"))-->> Hello "Corona SDK!"

    string.lower(s) / s:lower()


    Функция переводит все символы строки в нижний регистр:

    print(string.lower('Hello World!')) -->> hello world!

    string.upper(s) / s:upper()


    Функция переводит все символы строки в верхний регистр:

    print(string.upper('Hello World!')) -->>HELLO WORLD!

    string.rep(s, n) / s:rep(n)


    Функции передается строка и число n, возвращается строка повторенная n раз:

    local s = 'Corona '
    print(s:rep(3))-->> Corona Corona Corona

    string.reverse(s) / s:reverse()


    Функция возвращает переданную строку с символами в обратном порядке:

    print(string.reverse('9876543210')) -->0123456789

    string.match (s, pattern [, index])
    s:match(pattern [, index])


    Функция string.match находит вхождение в строке s согласно регулярного выражения pattern и возвращает параметры захвата. О захватах с помощью регулярных выражениях будет подробно описано в соответствующем разделе. В функции имеется необязательный параметр index который по умолчанию равен 1 и определяет из какого по счету вхождения в шаблон необходимо сделать захват, стоить заметить что отрицательное значение Index по аналогии с string.find не поддерживается:

    local reg = " (%w+):(%d+)"--регулярное выражение с захватом 2 параметров
    local s = " force:30, speed:25"--строка для поиска и захвата
    local key, value = s:match(reg, 2)--захват во втором вхождении
    print(key, value)-->> speed  25

    string.gmatch(s, pattern)
    s:gmatch(pattern)


    Функция string.gmacth во многом аналогична функции string.macth, но она предназначена для применения в качестве аргумента в цикле(более подробно о циклах будет рассмотрено в соответствующем разделе). string.gmacth циклически возвращает захваты от всех вхождений в строке, т.е. не требуется указывать index, выглядит это как на следующем примере:

    local reg = " (%w+):(%d+)"--регулярное выражение с захватом 2 параметров
    local s = " force:30, speed:25"--строка для поиска и захвата
    for key, value in s:gmatch(reg) do--цикл с захватом всех вхождений
    	print(key, value)
    end
    -->> force  30
    -->> speed  25

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

    string.sub(s, i [, j])
    s:sub(i [,j])


    Функция string.sub возвращает подстроку переданной строки. Подстрока начинается с i-го символа. Если третий аргумент j не указан, подстрока будет заканчиваться в конце строки. Если указан третий аргумент, подстрока заканчивается j-тым символом. Если i или j имеет отрицательное значение счет идет с конца строки. Приведу примеры использования:

    local s = 'To be or not to be'
    print(s:sub(7))-->>or not to be
    print(s:sub(7,12))-->>or not
    print(s:sub(-5))-->>to be
    print(s:sub(4,-3))-->>be or not to
    print(s:sub(-8,-4))-->>ot to

    string.gsub(s, pattern, replace [, n])
    s:gsub(pattern, replace [,n])


    Функция string.gsub одна из мощнейших функций языка. Функция имеет несколько вариантов применения в простейшем случае она заменяет в строке s все совпадения указанные в pattern на значение указанное в replace во всех местах, с помощью n можно ограничить число замен, функция вернет результирующую строку и количество замен:

    local s = 'dust ist fantastisch'
    print(s:gsub('s','S'))-->>duSt iSt fantaStiSch	4
    
    local s = 'lalalalala'
    print(s:gsub('a','A',3))-->>lAlAlAlala	3

    Как и в случае с string.find в качестве шаблона поиска может быть использовано регулярное выражение (захват). Аргументы захвата можно размещать согласно индексов %номер_индекса:

    print(string.gsub("lalalalala", "(l)(a)", "%2%1")) --захват символов l и a
    --далее вставка их в обратном порядке -->> alalalalal	5

    В качестве параметра replace может быть использована функция при этом захваченные регулярным выражением параметры будут передавать ей в качестве параметров.

    string.gsub("12 23 34", "(%d+)", print)--захват всех отдельно стоящих чисел
    -->> 12
    -->> 23
    -->> 34


    Несколько слов о utf-8.


    Большая часть функций библиотеки string не умеет корректно работать с форматом utf-8 по этой причине строки с национальной кодировкой и другими спецсимволами не будут верно обрабатываться. Для решения этих проблем в Corona SDK имеется отдельная библиотека utf, которая имеет как аналоги функций из библиотеки string так и несколько своих удобств. Более подробно эта библиотека будет рассмотрена в разрезе изучения Corona SDK, либо читатели могут изучить самостоятельно.

    Тип table


    Тип table (таблица) является самым основным типом представления данных в Lua, в самом общем смысле почти все основные сущности в языке являются таблицами, даже файлы исходных текстов. Как создавать таблицы и как с ними работать:

    local t = {}--создана пустая таблица
    t['key1'] = 'name1'--добавлен параметр key1 со зданием "name1"
    t['key2'] = {}--добавлен параметр key2 являющийся таблицей
    t['key2']['key3'] = 123--добавлен параметр key3 в таблицу key2

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

    local t = {
    	key1 = 'name1',
    	key2 = {
    		key3 = 123,
    	},
    }

    заметьте эти записи идентичный:

    t['key1'] = 'name1'
    t.key1 = 'name1'

    смысл в первом типе записи имеется только в том случае если в качестве ключа используется числовое значение:

    t[1] = 111--все верно
    t.1 = 111--ошибка так делать нельзя

    Значения записанные в таблице доступны для чтения и записи:

    print(t.key1)-->> name1
    t.key1 = 'name2'
    print(t.key1)-->> name2

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

    t = {}--содержимое таблицы очищено - это не лучший способ
    table.remove(t)--это хороший способ очистки таблицы

    Если вы хотите удалить один из ключей таблицы это можно сделать так же двумя способами:

    t.key1 = nil--присвоение nil удаляет ключ
    table.remove(t, key1)--это лучший способ очистки ключа

    Наиболее естественным способом инициализации таблиц в Lua счтается создание нумерованного массива, это делается следующим способом:

    local t = {23, 45, 3.14, 'vasia', {12, 'a'}}

    На самом деле если не указывать ключ при инициализации таблицы происходит нумерование в порядке увеличения начиная 1 (для большинства разработчиков более естественна нумерация с 0, но в Lua это не так, нужно об этом помнить), т.о. инициализированная таблица на самом деле имеет следующий вид:

    local t = {
    	[1] = 23,
    	[2] = 45,
    	[3] = 3.14,
    	[4] = 'vasia',
    	[5] = {
    		[1] = 12,
    		[2] = 'a',
    	},
    }

    Как видите «под капотом» с данными произошли достаточно существенные преобразования. У нумерованных таблиц в Lua имеются особые свойства, например добавив к имени таблицы символ # вы получаете количество строго нумерованных значений в таблице:

    print(#t)-->>5
    print(#t[5])-->>2 (размер вложенной таблицы с ключом 5)

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

    t[#t+1] = 'kolia'
    print(#t)-->> 6

    Если удалить один из ключей путем присвоения nil например ключ [3] непрерывная часть нумерованного массива уменьшится всего до 2 ключей [1] и [2], что не может привести к дальнейшим ошибкам из-за неверного понимания, по этой причине выше я советовал удалять ключи из таблицы с помощью table.remove так как при этом если удалить ключ остальные строгонумерованные значения будут смещены:

    --неверное удаление ключа
    t[3] = nil
    print(#t)-->> 2
    --верное удаление ключа
    table.remove(t,3)
    print(#t)-->> 5

    После правильного удаления ключа таблица будет иметь вид:

    local t = {
    	[1] = 23,
    	[2] = 45,
    	[3] = 'vasia',
    	[4] = {
    		[1] = 12,
    		[2] = 'a',
    	},
    	[5] = 'kolia',
    }

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

    t.key = 'name1'
    print(t.key, #t)-->> name1  5

    Для обхода всех нумерованных значений таблицы циклической операцией имеется функция ipairs, она используется в качестве аргумента цикла for по аналогии с string.gmatch:

    for key, value in ipairs(t) do
    	print(key, value)
    end
    -->> 1  23
    -->> 2  45
    -->> 3  vasia
    -->> 4  table: 00000000
    -->> 5  kolia

    Если же вы хотите обойти циклом все значения включая не строго нумерованные используйте функцию pairs:

    for key, value in pairs(t) do
    	print(key, value)
    end
    -->> 2  45
    -->> key  name1
    -->> 3  vasia
    -->> 1  23
    -->> 4  table: 00000000
    -->> 5  kolia

    Обратите внимание? что при использовании pairs последовательность вывода значений не строгая, а в общем-то довольно запутанная, это нужно учитывать. В заключении хочу отметить что использовать pairs/ipairs вы можете как передавай значение таблицы, так и создавая ее непосредственно в вызов примерно так:

    for key, value in ipairs{10,20,30} do
    	print(key, value)
    end
    -->> 1  10
    -->> 2  20
    -->> 3  30
    
    for key, value in pairs{key1 = 34,key2 = 65, key3 = 12} do
    	print(key, value)
    end
    -->> key1  34
    -->> key3  12
    -->> key2  65

    Тип function


    Тип function имеют все созданные пользователем функции и стандартные функции языка. Приведу пример:

    local summ = function(a,b)
    	return a + b
    end
    print(type(summ))-->> function
    print(type(print))-->> function

    Более подробно о порядке применения функций вы узнаете в соответствующем разделе.

    Тип thread


    Тhread — это отдельная нить выполнения кода. Попробую кратно пояснить что это такое. Весь код вашего проекта на Lua выполняется в едином нити выполнения, если есть необходимость выполнить в отдельной нити создается объект типа thread. Для создания используется следующий код:

    --создаем подпрограмму
    local co = coroutine.create(function ()
    	--код выполняемый в подпрограмме
    end)
    
    print('TYPE: '..type(co))-->> thread


    Как видите объект типа thread, но на самом деле код не разу не выполнен, что бы это исправить необходимо выполнить вызов coroutine.resume передав объект thread. В процессе выполнения кода может возникнуть необходимость прервать выполнения а дальше через какое-то время продолжить выполнения с того же места. Для выходы из выполнения с сохранением состояния функции используется функция coroutine.yield(), она выполняется без параметров внутри тела функции нити. Что бы обратно вернуть к выполнения кода необходимо заново выполнить coroutine.resume. Создадим пример в котором в теле подпрограммы будет содержаться вечный цикл с счетчиком, при каждой итерации будет происходить выход из подпрограммы. Т.е. код фактически будет считать количество раз которое выполнялось coroutine.resume для этого объекта.

    --создаем подпрограмму
    local co = coroutine.create(function ()
    	local num = 1--инициализируем стартовое значение счетчика итераций
    	while true do
    		print(num)--печать значения счетчика
    		num = num + 1--увеличение
    		coroutine.yield()--приостановка до следующего resume
    	end
    end)
    
    print('TYPE: '..type(co))
    --три раза вызываем продолжение выполнения
    coroutine.resume(co)-->> 1
    coroutine.resume(co)-->> 2
    coroutine.resume(co)-->> 3


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

    Тип userdata


    Тип userdata используется в Lua для связки исходников этого языка с исходными текстами написанными на других языках, в первую очередь C(си). Т.е. сложные пользовательские структуры оформленные в С в Lua принимаются как userdata. На уровне этой статьи этот тип нам не пригодится, но вы всегда можете самостоятельно изучить вопрос.

    Заключение


    Вы стали еще на шаг ближе к освоению языка Lua и теперь вы сможете уже уверено читать и понимать большую часть исходников которые вам встретятся при рассмотрении примеров, которые поставляются вместе с дистрибутивом Corona SDK, но все же это не все и я рекомендую изучить последний из трех уроков.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 20
    • 0
      Спасибо, интересная статья!
      • 0
        Спасибо за оценку! Статья стала квинтэссенцией защитных механизмов против шишек набитых мной в Lua за 5 лет, в Corona SDK за 3 года.
      • 0

        Вы не могли бы развить тему по поводу thread? Когда я имел дело с Lua — coroutine/thread были моделью stackful "продолжений". При resume происходило переключение стека и выполнение участка кода до следующего yield или завершения. Но никакого планировщика не было и в помине. Т.е. выглядело как непрозрачный вызов функции. Если же Corona SDK добавляет планировщик — неясно, как происходит ожидание асинхронного результата.

        • 0
          Насколько я понимаю в Corona SDK нет ничего нового для корутин и resume блокирует основной поток (и отрисовку экрана). Соответственно приложение будет зависать на время выполнения resume. (Небольшой пример)
          При использовании есть один существенный минус, если в коде перенесенном в нить возникнет ошибка вы об этом не узнаете

          Resume возвращает boolean флаг указывающий произошла ли ошибка. Если флаг установлен в false то вторым возвращаемым значением будет сообщение с деталями ошибки.
        • 0
          Не могли бы вы пояснить пример с coroutine.create?

          Согласно документации network.request уже является асинхронной функцией и смысла выносить её в отдельный поток я не вижу.
          • 0
            В действительности запросы network.request завешивают приложение и пример я приводил именно из личного опыта. По замыслу творцов Corona SDK таймер то же должен выполняться в отдельном контексте но вы можете сами провести эксперимент, разместите этот замечательный код где-нибудь в main, соберите приложение и попробуйте им пользоваться:
            timer.performWithDelay(100, function()
            network.request( урл ресурса с плохим пингом )
            end)

            Так же можно попробовать грузить файл на сервер по таймеру каждый тик таймера будет завешивать приложение. Мне короутины помогли. А вообще в примере нужно было просто привести пример объекта типа thread…

            • +1
              Возможно вы путаете network.request с библиотекой socket входящей в состав Corona? Код с использованием этой библиотеки выполняет синхронный (блокирующий) запрос:
              local http = require( "socket.http" )
              http.request{ ... }
              

              Единственная моя претензия: текущий пример thread не показывает преимущество корутины (если вы не используете coroutine.yield то весь код функции всё-равно выполнится за один раз и заблокирует обновление экрана до конца выполнения).
              • 0
                боюсь вы правы в обоих случаях… чуть позже доработаю статью. спасибо…
                да я проверил в том случае где была проблема я действительно использовал http.request на тот момент опыт был совсем никакой и использовал первое что попалось под руку… http.request действительно лагает и короутины решают с ним вопрос.
          • 0

            100501-я статья "lua-для-самых-маленьких". А где про корону-то?

            • 0
              На русском статей чуть меньше. В статье делается акцент на особенности применения Lua именно в короне. Да для совсем маленьких, но я нахожу такой вариант более приемлемым чем сразу начинать с 100 страницы. На мой взгляд корона в русскоязычном сообществе редкий гость, чего стоят группы в ВК с 500 подписчиками, т.е. писать статьи для «совсем больших» несколько преждевременно.
              • +1
                Самое то, что надо, чтобы не совершать неочевидных ошибок на первом этапе освоения нового движка. Так-то Lua красивый и мощный, но реализации стека в разных движках имеют особенности. И прошлый опыт может внезапно подкузьмить.
                • 0
                  Корона — это просто частный вариант реализации Lua. И как бы далеко не ушел в самом SDK, однажды упираешься в то, что без знания матчасти — никуда. А данная статья — как раз об этой самой матчасти. Просто и понятно.
                • +1
                  exe_com К слову, функция string.lower(s) (как и преобразование вверх) не работает с кириллицей. Для преобразования регистра в строке нужно предварительно использовать плагин utf8.
                  Спасибо за статью :)
                  • –1
                    Да вы правы, но эти тонкости я думал обсудить уже в разрезе изучения Corona SDK. Добавил пояснение. Спасибо.
                    • 0
                      Спасибо :) Этим вы выгодно отличите свою доку от великого множества остальных. Где функция уверенно приведена и якобы работает в полный рост, а за решением — все равно приходится идти на официальный форум, где люди работают преимущественно с латиницей и с такой проблемой никогда не сталкивались. У них итак все хорошо.
                      • +1
                        я в свое время решал проблемы с utf всякими самобытными костылями вроде этого:
                        utflen = function(ustring)
                        	local i = 0
                        	for uchar in string.gfind(ustring, "([%z\1-\127\194-\244][\128-\191]*)") do
                        		i = i + 1
                        	end
                        	return i
                        end
                        • 0
                          Да уж. Плагину utf8 всего-то годик-полтора, как помнится.
                  • 0
                    Как-то смутно описано удаление содержимого таблиц и ключей таблиц: «Это неправильно! Вот так правильно! Это хороший способ. А этот плохой.» Роберто в своей «Programming in Lua» утверждает что все способы хорошие, только по разному применимые. Или Вы описываете относительно Corona SDK? Тогда имеет смысл акцентировать на этом.
                    • +1
                      Статья рассчитана для новичков. Lua замечательный язык, но исходя из своего опыта мне кажется что не все одинаково допустимо. Я делаю советы типа «плохо / хорошо» и надеюсь что это поможет реже допускать сложные логические ошибки. Касаемо автора книги и языка, я ни в коем случае не оспариваю его авторитет, но нередко творение великих начинает жить своей жизнью сразу после первого выхода в свет и становится не ясно так ли действительно хотел творец. Взять к примеру не простую судьбу оператора goto который из версии в версию мигрирует в угоду просьбам пользователей и почти всегда является злом, пожалуй, кроме случая выхода из вложенного цикла.
                      • 0
                        Ну, раз уж нет continue, то и goto сгодится. Главное помнить, что он работает только под luajit и lua v. 5.2 и выше. А на 5.1 не работает. Я пробовал goto, мне понравилось, если честно.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое