Перейти к контенту

Справочник по функциям и классам


Рекомендуемые сообщения

По совету Artos-а решил изложить материал, касающийся работы с coroutine тут, поскольку данная таблица входит в пакет Lua СТАЛКЕР-а, и более подходящей темы трудно найти.

Нужность/ненужность данной информации оставлю на рассмотрение malandrinus-а, т.к. практическое использование подпрограмм пока находится под большим вопросом.

Тем не менее есть люди интересующиеся, поэтому продолжу...

Итак, обо всё по порядку (malandrinus-у. Можно я буду писать частями? Хочется сделать не только информативно, но и придать внешний вид. А вот со свободным временем у меня не очень.) :

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

Операции для работы с подпрограммами собраны в виде подбиблиотеки базовой библиотеки и содержатся внутри таблицы coroutine.

Эта таблица содержит всего шесть функций :

 

create

wrap

resume

yield

status

running

 

Я их подразделяю на основные :

create и wrap - создают подпрограмму

resume - запускает или продолжает выполнение подпрограммы

yield - приостанавливает выполнение подпрограммы

 

И служебные :

status - возвращает состояние подпрограммы

running - возвращает саму подпрограмму

 

Рассмотрим их подробнее.

coroutine.create (f)

 

Создает новую подпрограмму, с телом f.

Параметр f должен быть Lua функцией (или именем функции).

SubProg = coroutine.create (function()
                                -- code
                            end)

Или

function Body_SubProg()
    -- code
end

SubProg = coroutine.create (Body_SubProg)

Возвращает указатель на новую подпрограмму как объект типа нить ("thread").

(Не знаю как Вам будет лучше? Кто хочет для просмотра пишите в лог. Короче кому как удобнее.

Далее буду проверочный код писать именно в таком виде. Не все же умеют даже в лог писать.)

news_manager.send_tip(db.actor, type(SubProg)) -- Результат : "thread"

Функция create только создает новую подпрограмму, запуск подпрограммы не выполняется.

Он выполняется с помощью coroutine.resume (подробнее о этой функции ниже).

В примере ниже переменным n1 и n2 присваиваются соответственно значения 5 и 7

SubProg = coroutine.create(function (n1, n2)
                              local amount = n1+n2
                              return amount
                           end)
local bool, result = coroutine.resume(SubProg, 5, 7) -- Вызываем подпрограмму SubProg с параметрами 5 и 7
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(result)) -- Результат :  "true-->12"

Функция coroutine.create защищённая. Если вызвать эту функцию с ошибкой, то возвратится false и ошибка покажется как результат.

В данном примере аргументу n1 присваивается значение (number) 5, а аргументу n2 значение true (boolen).

Теперь попробуем сложить number и boolen.

SubProg = coroutine.create(function (n1, n2)
                              local amount = n1+n2
                           end)
-- и где-нибудь вызов
local bool, result = coroutine.resume(SubProg, 5, true)
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(result)) -- Результат : false-->А тут у каждого свои данные"

 

Подпрограмму также можно создать с помощью:

coroutine.wrap (f)

 

Она создает новую подпрограмму с телом f .

Параметр f должен быть Lua функцией (или именем функции).

SubProg = coroutine.wrap(function ()
                          -- code
                      end)

Или

function Body_SubProg()
    -- code
end

SubProg = coroutine.wrap(Body_SubProg)

Возвращает функцию, с помощью которой можно осуществить её запуск.

news_manager.send_tip(db.actor, type(SubProg)) -- Результат : "function"

Поскольку подпрограмма является функцией (в отличии от coroutine.create), то и вызываем её как функцию.

Параметры, передаваемые в неё в качестве дополнительных аргументов (в пр. ниже это 4 и 5), попадают в неявный вызов resume.

Эта функция возвратит только результат, т.е. первого булевого значения (как при вызове coroutine.resume) не будет :

function Body_SubProg(n1, n2)
    local amount = n1+n2
    return amount
end

SubProg = coroutine.wrap(Body_SubProg)

local result = SubProg(4, 5)
news_manager.send_tip(db.actor, tostring(result)) -- Результат : 9

coroutine.wrap не перехватывает ошибки - все ошибки попадают на уровень вызывающей функции. Т.е. она незащищённая.

Вызвав функцию (например) так :

local result = SubProg(4, nil)

Получите вылет (опять же - в отличии от подпрограммы, созданной при помощи coroutine.create).

 

coroutine.yield (···)

 

Приостанавливает выполнение вызываемой подпрограммы.

coroutine не может быть выполняющейся C функцией, метаметодом или итератором.

Любые параметры, передаваемые в yield в дальнейшем передаются как дополнительные параметры при вызове resume (об этом позже).

function Body_SubProg()
    coroutine.yield('pause')
    return 'end'
end

SubProg = coroutine.create(Body_SubProg)

local _,result = coroutine.resume(SubProg)
news_manager.send_tip(db.actor, tostring(result)) -- Результат : 'pause'

Разберём подробнее.

В строке local _,result = coroutine.resume(SubProg) происходит вызов подпрограммы.

Она начинает выполняться, и... "упирается" в строку coroutine.yield. Поэтому она приостанавливает выполнение и возвращает то, что находится в её аргументе ('pause').

Вернёмся к строке local _,result = coroutine.resume(SubProg). Вызов coroutine.resume(SubProg) "отработал". Она возвратила первое (обязательное) значение boolen и все значения, возвращаемые coroutine.yield. Поскольку из вышеупомянутой функции возвратилось одно значение ('pause'), соответственно оно и присвоиться переменной result.

Всё. На данный момент подпрограмма находится в "режиме ожидания".

 

Пошлём ей второй вызов :

local _,result = coroutine.resume(SubProg)
news_manager.send_tip(db.actor, tostring(result)) -- Результат : 'end'

Подпрограмма (функция, находящаяся в теле подпрограммы) продолжит своё выполнение с того места, где она "упёрлась". Т.е. начнёт выполнять код после coroutine.yield('pause').

В функцию coroutine.resume(SubProg) , ту, что вызывалась второй раз, возвращается строка 'end'.

И снова переменной result присваивается значение. Теперь уже строку 'end'

 

coroutine.resume (co [, ···])

 

Запускает или продолжает выполнение подпрограммы с указанным именем (co).

При первом использовании этой функции начинает выполняться тело функции.

Опциональные значения [, ···] передаются как параметры в эту функцию.

 

Если подпрограмма запускается без ошибок, resume возвращает true плюс любые значения переданные в yield (см. coroutine.yield) или любые значения возвращаемые функцией (если подпрограмма завершилась).

В случае возникновения ошибок, resume возвращает false плюс сообщение об ошибке (см. coroutine.create).

В принципе мы это уже "проходили", но всё же...

Ситуации :

1. Подпрограмма запустилась без ошибок, возвратила true и значение, возвращённое coroutine.yield :

function Body_SubProg(var)
    coroutine.yield(var)
    return 'end'
end

SubProg = coroutine.create(Body_SubProg)

local bool, result = coroutine.resume(SubProg, 'pause')
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(result)) -- Результат :  "true-->pause"

2. Подпрограмма запустилась без ошибок, возвратила true и значение, возвращённое функцией Body_SubProg.

Для этого вызовем coroutine.resume ещё раз - рестартуем её.

(В данном примере вызов coroutine.resume можно производить без дополнительных аргументов. Все они будут проигнорированы, т.к. сама функция Body_SubProg возвращает только одно значение - строку 'end'):

local bool, result = coroutine.resume(SubProg)
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(result)) -- Результат :  "true-->end"

3. Подпрограмма выполнилась с ошибкой, resume возвращает false плюс сообщение об ошибке :

function Body_SubProg(var)
    coroutine.yield(var+1)
    return 'end'
end

SubProg = coroutine.create(Body_SubProg)

local bool, result = coroutine.resume(SubProg, 'v')
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(result)) -- Результат :  "false-->тут путь к файлу, строка ошибки, описание ошибки"

Теперь будет немного сложнее (то, что откладывал на "ниже").

 

Если подпрограмма была в состоянии yield, resume рестартует подпрограмму, передавая ей параметры [, ···] а также результаты, полученные при вызове yield.

Сначала напишу код, затем его разберём :

function Body_SubProg(a, b)
    local c = coroutine.yield(a+b, a-b)
    return a, b, c
end

SubProg = coroutine.create(Body_SubProg)

local bool, var_1, var_2 = coroutine.resume(SubProg, 5, 2)
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(var_1)..'-->'..tostring(var_2)) -- Результат : "true-->7-->3"

local bool, var_1, var_2, var_3 = coroutine.resume(SubProg, "str")
news_manager.send_tip(db.actor, tostring(bool)..'-->'..tostring(var_1)..'-->'..tostring(var_2)..'-->'..tostring(var_3)) -- Результат : "true-->5-->2-->str"

В строке - local bool, var_1, var_2 = coroutine.resume(SubProg, 5, 2) - происходит первый вызов подпрограммы с именем SubProg.

Функция Body_SubProg ( a, b ) начинает работу с аргументами 5 и 2 для a и b соответственно.

В следующей строке объявляется переменная (local c), которой присваивается значение... и тут СТОП. Вызывается coroutine.yield.

А это значит что подпрограмма прерывается, так и не присвоив переменной c никакого значения.

Раз уж подпрограмма ждёт, то и мы подождём, что же из этого выйдет :-)

coroutine.yield( a+b, a-b ) возвращает в вызвавшую её функцию coroutine.resume(SubProg, 5, 2) результат своей работы ( a+b, a-b ), т.е. 7 и 3.

Как мы уже знаем, coroutine.resume(SubProg, 5, 2) присваивает переменной bool значение true, а переменным var_1 и var_2 принятые от yield - 7 и 3.

Ожидаем следующего вызова...

 

Он происходит в строке - local bool, var_1, var_2, var_3 = coroutine.resume(SubProg, "str").

И, как видим, в этом вызове происходит передача в функцию Body_SubProg нового (дополнительного) параметра - строки 'str'.

Именно значение этого параметра и присваивается переменной с.

Подпрограмма продолжила свою работу и строкой - return a, b, c - возвратила значения переменных a, b и c и закончила свою работу.

функция coroutine.resume(SubProg, "str") принимает эти возвращённые значения и присваивает их переменным var_1, var_2, var_3 соответственно.

 

 

 

Перед описанием оставшихся функций немного отвлечёмся.

Известно, что создав обычную функцию, в дальнейшем можно вызывать её сколько угодно раз.

С подпрограммами такое дело не проходит.

 

Она "живёт' только один цикл функции, находящейся в её теле (в нашем примере Body_SubProg).

Т.е. дойдя до последнего end-а этой функции все попытки последующего вызова будут приводить :

 

1. При создании подпрограммы с помощью wrap - к вылету.

2. При создании подпрограммы с помощью create - к возврату из resume двух значений, false и строки "cannot resume dead coroutine".

 

Второй вариант кажется не очень и критичным (вылета нет), но если вдруг функция, вызвавшая resume будет ждать от неё (скажем) число, а тут "на те - здрасьте", строка.

Результат может превзойти все ожидания.

Но одноразовая подпрограмма мало кого устроит.

Как поступить, если нужно использовать подпрограмму несколько раз, рассмотри после ознакомления с остальными функциями.

 

 

Теперь продолжим :

coroutine.running ()

Возвращает выполняемую подпрограмму, или nil, если вызвана из главной нити.

 

Тут мне сказать особо нечего, т.к. использовать ещё не было нужды.

Только пример :

function Body_SubProg()
    news_manager.send_tip(db.actor, 'SubProg-->'..tostring(SubProg)) -- Результат : "SubProg-->thread: тут ID подпрограммы SubProg"
    news_manager.send_tip(db.actor, 'coroutine.running-->'..tostring(coroutine.running())) -- Результат : "coroutine.running-->thread:  тут ID той подпрограммы, которая запущена"
end

SubProg = coroutine.create(Body_SubProg)

coroutine.resume(SubProg)
news_manager.send_tip(db.actor, 'main_thread-->'..tostring(coroutine.running())) -- Результат : "main_thread-->nil"

Оба сообщения, выводимые из тела функции Body_SubProg, после "thread", покажут один и тот же ID.

Этим самым мы убедимся в том, что подпрограмма SubProg и найденная запущенная подпрограмма являются одним и тем же объектом.

Сообщение из главной нити ("main_thread") покажет, что выполняющихся в данный момент подпрограмм нет (nil)

 

 

coroutine.status(co)

 

Возвращает статус подпрограммы co, в виде строки:

 

"running" (выполняется), если подпрограмма в данный момент выполняется

"suspended" (приостановалена), если подпрограмма приостановлена с помощью вызова yield, либо она еще не запущена на выполнение

"normal" (норма) если подпрограмма активна, но еще не запущена на выполнение (т.е. она активизирована другой подпрограммой)

"dead" (остановлена) если подпрограмма завершилась или если остановлена по ошибке.

 

Каждый элемент отдельно описывать не буду, а приведу все варианты в едином коде. Затем разберём :

AnySubProg = coroutine.create(function ()
                                 news_manager.send_tip(db.actor, '3-'..coroutine.status(SubProg)) -- 'normal'
                              end)
                      
SubProg = coroutine.create(function ()
                              news_manager.send_tip(db.actor, '2-'..coroutine.status(SubProg)) -- 'running'
                              coroutine.resume(AnySubProg)
                              coroutine.yield() -- coroutine.yield('s'+1)
                           end)

news_manager.send_tip(db.actor, '1-'..coroutine.status(SubProg)) -- 'suspended'
coroutine.resume(SubProg)
news_manager.send_tip(db.actor, '4-'..coroutine.status(SubProg)) -- 'suspended'
coroutine.resume(SubProg)
news_manager.send_tip(db.actor, '5-'..coroutine.status(SubProg)) -- "dead"

В результате на экране (или в логе, кто как) Вы увидите ряд сообщений :

 

1-suspended

2-running

3-normal

4-suspended

5-dead

 

Для облегчения навигации по коду я отметил строки с вызовом сообщений числами (типа ID) от 1 до 5 (напр. news_manager.send_tip(db.actor, '4-'..coroutine.status(SubProg)) ).

 

Итак, после создания подпрограммы сначала выдаётся сообщение с ID 1, которое напечатает suspended.

Это соответствует второй части определения suspended (см. описания стасусов), в частности "...либо она еще не запущена на выполнение".

Действительно, подпрограмма ещё не запускалась.

 

Следующей после неё строкой запускается подпрограмма SubProg, и в её теле выдаётся сообщение с ID 2 и результатом running.

Это также соответствует истине. Подпрограмма запущена и выполняется.

 

После этого запускается некая сторонняя подпрограмма AnySubProg в теле которой выдаётся сообщение с ID 3 - normal.

И снова верно. Подпрограмма SubProg на данный момент активна (мы же её уже вызвали), но не запущена.

Сейчас запущена другая (AnySubProg) подпрограмма, из которой и запрашивается статус SubProg.

 

После завершения работы AnySubProg, продолжается работа SubProg и строкой coroutine.yield() приостанавливает своё выполнение.

Работа первого вызова coroutine.resume(SubProg) завершается, и следующей строкой даёт сообщение с ID 4 - suspended

Теперь выполняется соответствие первой части определения suspended - "если подпрограмма приостановлена с помощью вызова yield..."

 

Вызвав подпрограмму SubProg второй раз (coroutine.resume(SubProg)), она завершает свою работу (последний end).

И теперь, в сообщении с ID 5 мы запрашиваем состояние подпрограммы, которая уже завершилась.

Естественно нам отвечают, что такая подпрограмма уже завершилась (первая часть определения "если подпрограмма завершилась..") - dead

 

Если изменить строку coroutine.yield() на coroutine.yield('s'+1) (т.е. делаем ошибку), то вместо сообщения 4-suspended будет сообщение 4-dead, соответствующее определению "...или если остановлена по ошибке"

 

 

Итак, необходимые знания у нас уже есть. Можно двигать дальше...

Изменено пользователем Cyclone
  • Полезно 1
Ссылка на комментарий

Ранее я рекомендовал не использовать операцию конкатенации цепочкой в стиле s1..s2..s3..s4, поскольку счёл из общих соображений, что должен происходить степенной рост времени выполнения и резкое замедление работы при росте числа соединяемых строк. Я это утверждение не проверил, и выяснилось, что я ошибался. RvP провёл тесты и сравнил три разных метода конкатенации. Привожу его тест для чистого Lua в сокращённом виде с результатами измерений в комментариях.

a = "s1"
b = "s2"
c = "s3"
d = "s4"
e = "s5"

t = os.clock()
for i=1, 1000000 do
    local q = table.concat({a, b, c, d, e})
end
print("table", os.clock() - t)        --2.684

t = os.clock()
for i=1, 1000000 do
    local q = a .. b .. c .. d .. e
end
print("concat", os.clock() - t)        --0.96s

t = os.clock()
for i=1, 1000000 do
    local q = string.format("%s%s%s%s%s", a, b, c, d, e)
end
print("format", os.clock() - t)        --4.3s

 

Видно, что format работает медленней всего. Я также провёл дополнительные тесты, которые показали, что все три приведённых методики обеспечивают линейный рост времени выполнения.

 

Выводы из всего этого.

Несколько неожиданная эффективность оператора цепочечной конкатенации очевидна связана с внутренней оптимизацией Lua. Поэтому вот такая строка

local q = a .. b .. c .. d .. e

не эквивалентна вот такому фрагменту

local q = a

q=q..b

q=q..c

q=q..d

q=q..e

во втором случае мы как раз получим степенной рост времени выполнения.

 

Почему медленней работает format. Очевидно это связано с расходами на парсинг строки формата. Хотя эта функция также обеспечивает линейный рост времени выполнения, но каждый параметр в строке формата очевидно съедает своё время.

 

Резюме. Продолжайте писать конкатенацию цепочкой, как делали раньше (впрочем, кто это вообще прочитает?) Рекомендую всё же не переносить эту практику на произвольный язык программирования, поскольку ещё раз, это работает нормально не само по себе, а в силу внутренней оптимизации именно здесь.

 

Также RvP обратил внимание на встроенный в Lua метод конкатенации произвольного числа строк с помощью table.concat (пример смотри выше в тестовом фрагменте). Этот способ даёт возможность соединять произвольное количество строк и обеспечивает линейный рост затрат с минимальными расходами почти сопоставимыми с базовым оператором конкатенации.

Я хочу ещё раз подчеркнуть, что слить произвольное количество строк с помощью конкатенации нормально не получится. Вот этот фрагмент:

local s = ""
for i=1,1000 do
   s = s .. <очередная строка для конкатенации>
end

Это не конкатенация цепочкой! Никакой оптимизации здесь нет, и деградацию производительности вы получите совершенно невероятную.

Это надо делать только так:

local t = {}
for i=1,1000 do
   table.insert(<очередная строка для конкатенации>)
end
s = table.concat(t)

 

Да ещё... Приятно, что хоть кто-то не только читает эту тему, но и относится критически к тому, что здесь пишется. Я как-то надеялся, что так будет с самого начала, но увы.

Изменено пользователем malandrinus
  • Полезно 2
 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Хотелось бы узнать конкретнее про ф-цию parent у игровый объектов. У меня она возвращала объект, у которого находится предмет.

К примеру:

local oParent = db.actor:object("bread"):parent()

Вернет объект актора.

 

Так а что ещё конкретнее надо? Всё сам и сказал. Где объект лежит, того и вернёт. Очевидно имеет смысл только для инвентарных предметов.

И кстати, я это писал в посте про game_object.

Malandrinus

Изменено пользователем malandrinus
Ссылка на комментарий

malandrinus, RvP

По следам разбора конкатенации...

Найду свободное время - поэкперементирую, но пока вот мои результаты в SciTE (??? не вписываются в заключение ???) :

table     1.875
concat    0.344
format    1.5

 

Добавлено через 9 мин.:

Ага. Увеличил размер строки и всё стало на свои места.

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

Разберу - отпишусь конкретнее.

Изменено пользователем Gun12
Ссылка на комментарий

Да, попробуйте сделать строки длиной по 99 символов (ужасная задержка), а затем по 100 (обыгрывает concat) и сравните результаты. Это только у меня format так себя ведет?

Изменено пользователем Gun12
Ссылка на комментарий

Gun12, при длине всех строк 99 символов(строки разные), имею результат:

table 1.781

concat 0.734

foramt 8.719

 

а при длине 100 формат выполняется за 1.4, непонятно...

Изменено пользователем RvP

Vita sine libertate, nihil

Vita sine litteris - mors est

Ссылка на комментарий

Всё дело в том, что в зависимости от длины строки, модификатор 's' обрабатывается разными методами. Вот кусок кода, отвечающего за данный модификатор, из исходника метода обработки функции string.format:

case 's': {
          size_t l;
          const char *s = luaL_tolstring(L, arg, &l);
          if (!strchr(form, '.') && l >= 100) {
            /* no precision and string is too long to be formatted;
               keep original string */
            luaL_addvalue(&b);
            break;
          }
          else {
            nb = sprintf(buff, form, s);
            lua_pop(L, 1);  /* remove result from 'luaL_tolstring' */
            break;
          }
        }

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

singapur22

Вот спасибо. Теперь можно внести дополнение в определение - "При работе с заведомо длинными строками (100 и более символов) следует использовать

string.format, который будет работать быстрее чем table.concat"

Ссылка на комментарий

Теперь я забираю свои слова, сказанные выше, назад.

Если поставить все тестовые варианты в равные условия, то результаты сильно изменяться.

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

Для string.format создал идеальные условия, т.к. иначе проигрыш будет слишком очевиден.

a = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaa" --100 символов
b = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbb" --100 символов
c = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
cccccccccccccccccccccccc" --100 символов
d = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddd" --100 символов
e = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
eeeeeeeeeeeeeeeeeeeeeeee" --100 символов

tb = {a, b, c, d, e} -- общая для всех таблица

t = os.clock()
for i=1, 1000000 do
    local q = table.concat(tb)
end
t2 = os.clock() - t
print("table", t2)

t = os.clock()
for i=1, 1000000 do
    local q = tb[1] .. tb[2] .. tb[3] .. tb[4] .. tb[5]
end
t2 = os.clock() - t
print("concat", t2)

t = os.clock()
for i=1, 1000000 do
    local q = string.format("%s%s%s%s%s", unpack(tb))
end
t2 = os.clock() - t
print("format", t2)

Результат

table     0.954
concat    1.015
format    1.547

 

 

Изменено пользователем Gun12
Ссылка на комментарий
Gun12, Хм. А теперь замерь время работы функции unpack() и вычти результат от значения пременной t2. Только в этом случае ты получишь истиное время выполнения функции format.

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

Изначально, вместо unpack я писал tb[1], tb[2], tb[3], tb[4], tb[5].

Результат был ещё хуже. Как бы ещё немного помог ей.

P.S.

Что-то мне подсказывает, что мы не в той теме общаемся. Нам бы в "Скриптование..." перейти.

Изменено пользователем Gun12
Ссылка на комментарий

Gun12, Поэкспериментировал с вашей функцией format. Максимальную скорость получил только в данном случае:

local a = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaa" --100 символов
local b = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbb" --100 символов
local c = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
cccccccccccccccccccccccc" --100 символов
local d = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddd" --100 символов
local e = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
eeeeeeeeeeeeeeeeeeeeeee" --100 символов

function f(...)
    t = os.clock()
    local q
    for i=1, 100000 do
        q = string.format("%s%s%s%s%s", ...)
    end
    print("format", os.clock() - t)
end

f(a,b,c,d,e)

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

Но и в этом случае, конкатенация работает немногим быстрее.

 

P.S.

Я бы посоветовал создать тему, непосредственно связанную с языком Lua.

 

Изменено пользователем singapur22

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

singapur22, Gun12

А я бы осмелился посоветовать подобные разборки/ковырялки в скриптах/классах/методах/функциях проводить в рабочих топиках типа "Скриптование, ...", откуда позже уже проверенные и важные материалы вносить в "Справочник ...".

А излишки/издержки - можно будет подчищать и в рабочем топике.

ИМХО. ;)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Если-бы у меня спросили о темах постоянно возникающих у меня вопросов - я бы ответил так:

 

x-Ray: программирование на Lua.

x-Ray: создание, настройка и управление объектами.

x-Ray: создание интерфейсов.

 

[X]

 

 

 

 

 

Изменено пользователем 7.9

всё легко

Ссылка на комментарий

Не один раз на разных форумах задавали вопрос о том что-же такое шаблон и с чем его едят.

Совсем недавно подобный вопрос всплыл и тут. Вот и решил , за отсутствием специализированного топика по lua, выложить материал тут.

Но прежде всего хочу сказать, что всё сказанное мною относиться к заре моего познавания Lua.

Сейчас нет времени исправлять то, чего не знал, не так объяснил и не правильно понимал. Поэтому выкладываю как было на тот момент.

Всё же лучше чем сухие предложения из официального мануала.

С вопросами (а также тонкостями) составления шаблонов обращаться либо в ПМ, либо (лучше в плане всеобщего образования) в теме "Скриптование, спавн и логика ".

Хватит лирики, вот ссылка на страничку из моего манула по Lua 2-x двухгодичной давности :

http://ifolder.ru/25675390

Изменено пользователем Gun12
Ссылка на комментарий

И ещё раз о скоростях вычислений в Lua. В частности затронем тему работ с таблицами.

Как не странно, работая с таблицами (особенно с "массивными"), нужно учитывать, как мы её будем итерировать, в том, или ином случае.

Строго-индексированные таблицы, многие предпочитают итерировать посредством функции ipairs(). Данный способ наиьолее удобный, но работает он в два раза медленнее стандартного числового итерирования:

function ft1()
    local t = os.clock()
    for i=0, 100000 do
        for j,v in ipairs(tbl1) do end
    end
    local tr = os.clock() - t
    print("ipairs", tr)
end

function ft2()
    local t = os.clock()
    local c = #tbl1
    local j
    for i=0, 100000 do
        for k=1, c do j = tbl1[k] end
    end
    local tr = os.clock() - t
    print("count", tr)
end

ft1()
ft2()

Результат:

ipairs    0.109
count    0.063

 

Далее. Имеется различие в скорости считывания данных из таблицы, при числовых и строчных ключах. Работая с алгоритмами матриц, обратил внимание, что строчные ключи считываются на ~10...50% быстрее, чем числовые(индексированные), не зависимо от длин имён. По крайней мере, при длинах имён в 1 и 10 символов, различий замечено не было:

local tbl = {[1] = 55, A = 44, Abcdefghij = 99}

function fi()
    local t = os.clock()
    local j
    for i=0, 1000000 do j = tbl[1] end
    local tr = os.clock() - t
    print("indices", tr)
end

function fk()
    local t = os.clock()
    local j
    for i=0, 1000000 do j = tbl.A end
    local tr = os.clock() - t
    print("keys1", tr)
end

function fk2()
    local t = os.clock()
    local j
    for i=0, 1000000 do j = tbl.Abcdefghij end
    local tr = os.clock() - t
    print("keys2", tr)
end

fi()
fk()
fk2()

Результат:

indices    0.078
keys1    0.031
keys2    0.031

Считывание строчных ключей аналогично по скорости, как через точку (tbl.key), так и через квадратные скобки (tbl["key"]).

Изменено пользователем singapur22

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

singapur22,

:)

    for i=0, 1000000 do j = tbl[1] end

Время то хоть зависит от длины цикла?

 

Далее. Имеется различие в скорости считывания данных из таблицы, при числовых и строчных ключах. Работая с алгоритмами матриц, обратил внимание, что строчные ключи считываются на ~10...50% быстрее, чем числовые(индексированные), не зависимо от длин имён. По крайней мере, при длинах имён в 1 и 10 символов, различий замечено не было:

Следующий тест не проверяет, что индексированные медленнее. У тебя там нет никакого индексированного доступа, так как это уже хеш-таблица.

Изменено пользователем abramcumner
Ссылка на комментарий
u - знаковое значение

s - беззнаковое значение

Скорее наоборот.

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

Сохранив в пакет через метод u8 число -10, а прочитав с помощью s8, я получил 0, читая с помощью u8 - 246.

То есть нельзя сохранить и прочитать отрицательное число?

 

malandrinus, Извиняюсь, всё работает.

Изменено пользователем Real Wolf
Ссылка на комментарий

Real Wolf,

Скорее наоборот.

Очепятался. Спасибо, что заметил.

 

Сохранив в пакет через метод u8 число -10, а прочитав с помощью s8, я получил 0, читая с помощью u8 - 246.

Это странно, поскольку я проверил весь допустимый диапазон [-128, 127] и работает именно так, как я написал. Т.е. пишем с помощью w_u8 отрицательное число и без проблем читаем его с помощью r_s8. Какой движок используешь для теста? Приведи пожалуйста код.

 

P.S.: информацию в посте поправил.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Недавно, изучая нет-пакеты, наткнулся на описание интерфейса "cse_alife_inventory_item", некоторые его параметры вызывают любопытсво, особенно: кватернионы, угловые и линейные скорости, импульсы, позиции - всё это явно физика. Никто не сможет рассказать, как это всё связано с "инвентарностью" объекта?

 

cse_alife_inventory_item
{ name => 'condition', type => 'f32', default => 0.0 }, # 0xec
{ name => 'upgrades', type => 'l32szv', default => [] }, # 0xec

{ name => 'upd:num_items', type => 'h8' }, # 0x90
{ name => 'upd:force', type => 'f32v3' }, # 0x58
{ name => 'upd:torque', type => 'f32v3' }, # 0x58
{ name => 'upd:position', type => 'f32v3' }, # 0x58
{ name => 'upd:quaternion', type => 'f32v4' }, # 0x5
{ name => 'upd:angular_velocity', type => 'f32v3' }, # 0x58
{ name => 'upd:linear_velocity', type => 'f32v3' }, # 0x58
{ name => 'upd:enabled', type => 'u8'}, # 0x58
{ name => 'upd:quaternion_SOC', type => 'q8v4' }, # 0x5
{ name => 'upd:angular_velocity_SOC', type => 'q8v3' }, # 0x28,0x2c,0x30
{ name => 'upd:linear_velocity_SOC', type => 'q8v3' }, # 0x28,0x2c,0x30
{ name => 'upd:condition', type => 'f32' }, # 0x90
{ name => 'upd:timestamp', type => 'u32' }, # 0x58
{ name => 'upd:num_items_old_format', type => 'u16' }, # 0x58
{ name => 'upd:cse_alife_inventory_item__unk1_u8', type => 'u8' }, # 0x58

 

Изменено пользователем 7.9

всё легко

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

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

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
  • Куратор(ы) темы:

×
×
  • Создать...