Это популярное сообщение. Malandrinus 615 Опубликовано 8 Июля 2009 Это популярное сообщение. Поделиться Опубликовано 8 Июля 2009 (изменено) В данной теме собраны сведения по скриптовой модели сталкера: функции и классы, методы и свойства, взаимосвязь классов и последовательность работы с ними, связь работы классов и файлов конфигураций. К наполнению темы приглашаются все желающие. В наполнении темы непосредственно участвовали и существенно мне помогли: @Monnoroch, @Kolmogor, @Unnamed Black Wolf, @меченый(стрелок), @IQDDD, @Kirag, @Taroz, @dan, @7.9, @Garry_Galler, @AKKK1, @Bak и много других людей. Скрытый текст Скрытый текст класс alife_simulator. Базовые операции с серверными объектами. Пространства имён. Глобальные функции для большого числа задач. "Создание своего класса" и "Наследование от экспортированных классов". Базовые сведения об объектно-ориентированном программировании для сталкера. Необходимо прочитать, для понимания темы про биндер и некоторых других. В одном посте: Общие слова об архитектуре и скриптовой модели сталкера "Класс object_binder" расширение онлайновых объектов, колбеки, сохранение состояния. "Класс net_packet" Регистрация скриптовых классов с помощью object_factory Серверные классы. Часть 1 Иерархия серверных классов, описание не закончено. Серверные классы. Часть 2 Картинка структуры наследования и несколько заключительных слов Клиентские классы Скрытый текст Класс game_object Интерфейс ко всем онлайновым (клиентским объектам) Класс hit для нанесения урона скриптом и другая информация (IQDDD) Некоторая информация по управлению путями патрулирования здесь (Kirag) и здесь (Taroz) Неполная информация по управлению памятью неписей с примером здесь (Bak) Физическая оболочка объектов (Garry_Galler) Пост о выборе (подборе) оружия НПС и стрельбы (*Shoker*) Скрытый текст Управление заданиями Класс CGameTask и другие вспомогательные классы и функции. Управление инфопорциями Функции, колбеки, форматы файлов Список специальных системных инфопорций (Unnamed Black Wolf) Система профилей и алгоритм генерации имён. Форматы файлов, функции Дополнительная информация по параметрам профилей terrain_sect (Kolmogor) Диалоги. Часть 1 Форматы файлов, базовые сведения Диалоги. Часть 2 Скриптовые диалоги Диалоги. Часть 3 Тематическая подборка функций управления диалогами Скрытый текст Функции времени Тематическая подборка функций, связанных с управлением игровым временем. Класс CTime Вспомогательный класс для управления игровым временем Полезная скриптовая функция с использованием CTime (Garry_Galler) Скрытый текст class ini_file (меченый(стрелок)) Класс FS и CSavedGameWrapper Бинарный доступ к файлам, в том числе в игровых архивах, управление сохранёнными играми. Скрипт уровня. Забытая фишка с колбеком на заход на уровень Класс vector Некоторая полезная информация о разных вещах (меченый(стрелок)) "Класс render_device" Направление и положение камеры, характеристики экрана, программная пауза игры и др. Некоторая информация о различиях между ТЧ и ЗП в системе оконных классов и колбеков. (lekzd) Неплохо бы развить эту тему! Некоторая полезная информация о скриптовых функциях из модуля _g.script. (lekzd) Также требует развития! Полезные функции для работы с графом игры (Garry_Galler) В одном посте: Класс profile_timer Отладочные измерения скорости работы фрагментов программы Класс client_spawn_manager Колбек на выход в онлайн без использования биндера. Работа с консолью. Класс CConsole Анимации цвета. Класс color_animator Всякие моргающие элементы в окнах и пр. Управление постэффектами. Скриптовые постэффекты. Класс effector Класс sound_object. Проигрывание звуков в игре в произвольном месте, от произвольного объекта, в голове актора. (Shadows) Пост удалён автором (прим. Kirgudu) Скрытый текст Оконные классы Некоторая общая информация о создании окон Список методов, XML-тегов и событий для оконных классов (ТЧ/ЧН/ЗП) (Norman Eisenherz) Представление материала в моих статьях оптимизировано для онлайнового просмотра. Если кому не хочется лазить по спойлерам, а нужно просмотреть текст какого-либо поста "потоком", то могу рекомендовать просмотр в режиме "текстовая версия". В этом же режиме удобно сохранять содержимое темы на диск. (прим. Malandrinus) Изменено 30 Июля пользователем Kirgudu 5 5 16 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Nazgool 250 Опубликовано 31 Августа 2011 Поделиться Опубликовано 31 Августа 2011 (изменено) По совету 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, соответствующее определению "...или если остановлена по ошибке" Итак, необходимые знания у нас уже есть. Можно двигать дальше... Изменено 2 Сентября 2011 пользователем Cyclone 1 Ссылка на комментарий
Malandrinus 615 Опубликовано 4 Сентября 2011 Автор Поделиться Опубликовано 4 Сентября 2011 (изменено) Ранее я рекомендовал не использовать операцию конкатенации цепочкой в стиле 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) Да ещё... Приятно, что хоть кто-то не только читает эту тему, но и относится критически к тому, что здесь пишется. Я как-то надеялся, что так будет с самого начала, но увы. Изменено 4 Сентября 2011 пользователем malandrinus 2 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Real Wolf 34 Опубликовано 4 Сентября 2011 Поделиться Опубликовано 4 Сентября 2011 (изменено) Хотелось бы узнать конкретнее про ф-цию parent у игровый объектов. У меня она возвращала объект, у которого находится предмет. К примеру: local oParent = db.actor:object("bread"):parent() Вернет объект актора. Так а что ещё конкретнее надо? Всё сам и сказал. Где объект лежит, того и вернёт. Очевидно имеет смысл только для инвентарных предметов. И кстати, я это писал в посте про game_object. Malandrinus Изменено 4 Сентября 2011 пользователем malandrinus Ссылка на комментарий
Nazgool 250 Опубликовано 5 Сентября 2011 Поделиться Опубликовано 5 Сентября 2011 (изменено) malandrinus, RvP По следам разбора конкатенации... Найду свободное время - поэкперементирую, но пока вот мои результаты в SciTE (??? не вписываются в заключение ???) : table 1.875 concat 0.344 format 1.5 Добавлено через 9 мин.: Ага. Увеличил размер строки и всё стало на свои места. malandrinus, удали пожалуйста мой пост. При разных длинах строк происходят совершенно интересные вещи. Разберу - отпишусь конкретнее. Изменено 5 Сентября 2011 пользователем Gun12 Ссылка на комментарий
Nazgool 250 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) Да, попробуйте сделать строки длиной по 99 символов (ужасная задержка), а затем по 100 (обыгрывает concat) и сравните результаты. Это только у меня format так себя ведет? Изменено 6 Сентября 2011 пользователем Gun12 Ссылка на комментарий
RvP 1 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) Gun12, при длине всех строк 99 символов(строки разные), имею результат: table 1.781 concat 0.734 foramt 8.719 а при длине 100 формат выполняется за 1.4, непонятно... Изменено 6 Сентября 2011 пользователем RvP Vita sine libertate, nihil Vita sine litteris - mors est Ссылка на комментарий
singapur22 14 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 Всё дело в том, что в зависимости от длины строки, модификатор '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; } } Опаа-а!!! Ливер вылез! Ссылка на комментарий
Nazgool 250 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 singapur22 Вот спасибо. Теперь можно внести дополнение в определение - "При работе с заведомо длинными строками (100 и более символов) следует использовать string.format, который будет работать быстрее чем table.concat" Ссылка на комментарий
Nazgool 250 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) Теперь я забираю свои слова, сказанные выше, назад. Если поставить все тестовые варианты в равные условия, то результаты сильно изменяться. Изначально я создал таблицу строк, а потом использовал обращение к ней из каждого варианта. Для 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 Изменено 6 Сентября 2011 пользователем Gun12 Ссылка на комментарий
singapur22 14 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 Gun12, Хм. А теперь замерь время работы функции unpack() и вычти результат от значения пременной t2. Только в этом случае ты получишь истиное время выполнения функции format. Опаа-а!!! Ливер вылез! Ссылка на комментарий
Nazgool 250 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) Изначально, вместо unpack я писал tb[1], tb[2], tb[3], tb[4], tb[5]. Результат был ещё хуже. Как бы ещё немного помог ей. P.S. Что-то мне подсказывает, что мы не в той теме общаемся. Нам бы в "Скриптование..." перейти. Изменено 6 Сентября 2011 пользователем Gun12 Ссылка на комментарий
singapur22 14 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) 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. Изменено 6 Сентября 2011 пользователем singapur22 Опаа-а!!! Ливер вылез! Ссылка на комментарий
Artos 99 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) singapur22, Gun12 А я бы осмелился посоветовать подобные разборки/ковырялки в скриптах/классах/методах/функциях проводить в рабочих топиках типа "Скриптование, ...", откуда позже уже проверенные и важные материалы вносить в "Справочник ...". А излишки/издержки - можно будет подчищать и в рабочем топике. ИМХО. Изменено 6 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
7.9 174 Опубликовано 6 Сентября 2011 Поделиться Опубликовано 6 Сентября 2011 (изменено) Если-бы у меня спросили о темах постоянно возникающих у меня вопросов - я бы ответил так: x-Ray: программирование на Lua. x-Ray: создание, настройка и управление объектами. x-Ray: создание интерфейсов. [X] Изменено 6 Сентября 2011 пользователем 7.9 всё легко Ссылка на комментарий
Nazgool 250 Опубликовано 10 Сентября 2011 Поделиться Опубликовано 10 Сентября 2011 (изменено) Не один раз на разных форумах задавали вопрос о том что-же такое шаблон и с чем его едят. Совсем недавно подобный вопрос всплыл и тут. Вот и решил , за отсутствием специализированного топика по lua, выложить материал тут. Но прежде всего хочу сказать, что всё сказанное мною относиться к заре моего познавания Lua. Сейчас нет времени исправлять то, чего не знал, не так объяснил и не правильно понимал. Поэтому выкладываю как было на тот момент. Всё же лучше чем сухие предложения из официального мануала. С вопросами (а также тонкостями) составления шаблонов обращаться либо в ПМ, либо (лучше в плане всеобщего образования) в теме "Скриптование, спавн и логика ". Хватит лирики, вот ссылка на страничку из моего манула по Lua 2-x двухгодичной давности : http://ifolder.ru/25675390 Изменено 10 Сентября 2011 пользователем Gun12 Ссылка на комментарий
singapur22 14 Опубликовано 11 Сентября 2011 Поделиться Опубликовано 11 Сентября 2011 (изменено) И ещё раз о скоростях вычислений в 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"]). Изменено 11 Сентября 2011 пользователем singapur22 Опаа-а!!! Ливер вылез! Ссылка на комментарий
abramcumner 1 156 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) singapur22, for i=0, 1000000 do j = tbl[1] end Время то хоть зависит от длины цикла? Далее. Имеется различие в скорости считывания данных из таблицы, при числовых и строчных ключах. Работая с алгоритмами матриц, обратил внимание, что строчные ключи считываются на ~10...50% быстрее, чем числовые(индексированные), не зависимо от длин имён. По крайней мере, при длинах имён в 1 и 10 символов, различий замечено не было: Следующий тест не проверяет, что индексированные медленнее. У тебя там нет никакого индексированного доступа, так как это уже хеш-таблица. Изменено 13 Сентября 2011 пользователем abramcumner Ссылка на комментарий
Real Wolf 34 Опубликовано 29 Сентября 2011 Поделиться Опубликовано 29 Сентября 2011 (изменено) u - знаковое значение s - беззнаковое значение Скорее наоборот. Обратите внимаение, что для типа s8 (знаковый байт) нет метода записи. Подозреваю, что вместо него можно использовать соответствующий метод для беззнакового типа. Сохранив в пакет через метод u8 число -10, а прочитав с помощью s8, я получил 0, читая с помощью u8 - 246. То есть нельзя сохранить и прочитать отрицательное число? malandrinus, Извиняюсь, всё работает. Изменено 29 Сентября 2011 пользователем Real Wolf Ссылка на комментарий
Malandrinus 615 Опубликовано 29 Сентября 2011 Автор Поделиться Опубликовано 29 Сентября 2011 Real Wolf, Скорее наоборот. Очепятался. Спасибо, что заметил. Сохранив в пакет через метод u8 число -10, а прочитав с помощью s8, я получил 0, читая с помощью u8 - 246. Это странно, поскольку я проверил весь допустимый диапазон [-128, 127] и работает именно так, как я написал. Т.е. пишем с помощью w_u8 отрицательное число и без проблем читаем его с помощью r_s8. Какой движок используешь для теста? Приведи пожалуйста код. P.S.: информацию в посте поправил. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
7.9 174 Опубликовано 10 Октября 2011 Поделиться Опубликовано 10 Октября 2011 (изменено) Недавно, изучая нет-пакеты, наткнулся на описание интерфейса "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 Изменено 10 Октября 2011 пользователем 7.9 всё легко Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти