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

Язык Lua. Общие вопросы программирования


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

С чего начинать и где взять.

 

Установка Lua:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=629106

 

Руководство «Программирование на языке Lua», третье издание:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=905308

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

*Shoker*

Неявная передача возможна при вызове метода, т.е. посредством двоеточия.

При этом неявной переменной self будет соответствовать значение типа таблица или юзердата.

В чистом lua это делается просто.

В таблице _G всегда есть поля с загруженными модулями (файлами). Из этих полей можно "выдрать" название модуля. (если интересно, то могу показать)

А вот в Сталкере...лень искать. Возможно есть что-то похожее на _M. Ну там this._NAME что-ли :-)

Artos

Но тут ведь школа...И тем более по lua. Так что есть смысл изучать серьёзнее.

 

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

Shoker

А в Lua случаем нету возможности неявно передать в функцию название скрипта, откуда она была вызвана?

Вопрос задан неоднозначно. Из скрипта ничего не вызывается, вызывается из функции. Поэтому непонятно, что такое "скрипт, откуда вызвана функция". Ты имеешь в виду файл, где находится функция, или имеешь в виду именно функцию, откуда вызвана текущая?

 

Если нужен файл, содержащий данную функцию, то в сталкере имеется функция script_name(), которая возвращает имя текущего файла без расширения. Если же нужно имя вызвавшей функции, то это в общем случае может быть не выполнимо, поскольку у функции может и не быть имени вообще, как например в случае, если вызов был прямо из движка, или функция была определена только своим телом без имени или вообще была скомпилена на лету с использованием loadstring. Если уж совсем надо, то нужны отладочные возможности из debug, как советовал Artos. Однако использовать отладочные возможности в неотладочных целях - порочная практика: ненадёжно, ресурсоёмко, делает код малопереносимым и малопонятным. Типичный пример индусского подхода.

 

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

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

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

 

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

Пожалуйста, обновите ссылку на скрипты из этого поста или дайте новую.

malandrinus, пытался отправить тебе ЛС, не вышло, ящик забит?

 

ТЧ 1.0004. SAP и Trans mod

github

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

А вот для нетпектов "простых обёрток для управления параметрами объектов в процедурно-ориентированном стиле", что-то до сих пор и нету...

И вообще!.. :negative:

 

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

всё легко

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

Есть ли в Lua возможность пропустить шаг в цикле? Приведу пример из php

foreach ($array as $k => $v ) {
   ...
   if (...) {
    continue // код ниже не будет выполнятся для текущей пары k,v
   }
   ...
}

Надеюсь, понятно объяснил

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

Shredder, конкретного оператора нет, но это можно с имитировать.

for i=1, 3 do
   if i ~= 2 then
       print (i)
   end
end

Результат понятен - пропускается цикл со значением счётчика равному двум.

Конечно, это далеко не универсальный вариант, но он работает.

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

Да это понятно. Только как раз наличие такого оператора дало бы возможность уменьшить вложенность, как ранний return в функции. Эх, жаль конечно.

Я тут недавно наткнулся на интересный вариант итерации таблиц table.foreach(table, function(k,v) ... end). Если пользоваться таким способом, то тут ранний return решает проблему вложенности. А вот как со скоростью по сравнению с pairs (ipairs)? Есть ли какие-нибудь подводные камни?

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

Shredder

Дык, а table.foreach ничем не отличается от того же итератора pairs.

Для foreach передается два параметра: первый таблица, второй - функция, в функцию же автоматом передаётся два значения: ключ и значение этого ключа. И так происходит для всей таблицы, т.е. в функцию передаётся поочерёдно все ключи указанной таблицы.

Абсолютно тоже самое происходит и с конструкцией вида:

for key, value pairs(table) do
-- здесь по сути тело функции, которая передавалась вторым параметром для foreach.
end

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

P.S. А к стати table.foreach в игре осталась? Вроде как её и table.foreachi нет с какой-то версии lua.

Сейчас глянул в 5.0 ещё были, в 5.1 уже исключили из стандартного набора. В игре какая версия используется?

 

Ещё заглянул в _G в 1.0006. Там функция foreach ещё есть. Но в любом случае - не вижу смысла её использовать...

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

В ЗП эта функция есть, и раз нет ничего такого, чтобы мешало её использовать, отныне буду все итерации таблиц делать с её применением, чтобы код выглядел более читабельным.

 

Дабы не разводить чат в теме пишу в твоём же посте (эх, служебное положение).

Не соглашусь с тем, что функция итератор foreach выглядит более читабельно нежели pairs.

Но, раз тебе нравится - пожалуйста, никто собственно и не возражает...

ColR_iT

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

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

Ну если бы я был автором этого языка, то неужели написал бы на С заведомо медленную функцию?

В среднем foreach и pairs работают одинаково быстро. Остальное зависит от... платформы, процессора.. и Бог его знает чего ещё. Например у меня foreach работает на пару десятых(сотых) долей секунды быстрее (при десятимилионном цикле). Тем не менее логичнее и понятнее, для меня, использовать pairs.

Но тут уж каждому своё.

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

Кину и свои 5 копеек ;-)

Не знаю где нашли ColR_iT и Shredder в кодах игры применение table.foreach иль table.foreachi, но в оригинальных кодах их нет ни в какой версии релизов ТЧ/ЧН/ЗП и ни в каком патче ТЧ.

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

И на вкус и цвет конечно же товарисЧей нет, но читабельность кода зависит и от того, 'знакомы' ли встречающиеся по тексту функции/методы иль это экзотика. Если основная масса скриптов в приложении постоена на использовании (i)pairs, то врядли повысится читабельность от навязывания устаревших foreach(i). Каждый язык имеет свой синтаксис и свою "читабельность", и попытки читать/писать как на другом, привычном кому-то языке - это просто напросто субъективизм.

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

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

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

7.9

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

 

Когда мне это всё надоело, я и создал обёртку с использованием ООП для классов wxLua.

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

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

Shredder,

Есть ли в Lua возможность пропустить шаг в цикле?

В версии 5.2 добавили goto, который решает эту задачу. Ну а в сталкере придётся извращаться. К примеру так (не претендую на оптимальность, просто как вариант):

for i = 1,7 do (function()
   -- ...
   if i == 3 then return end
   -- ...
end)() end

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

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

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

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

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

 

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

Приведу пример, что для меня значит "читабельность" кода. Фрагмент из treasure_manager.script (ТЧ)


for k,v in pairs(self.treasure_info) do
if v.done == false then
local treasure_prob = xr_logic.pick_section_from_condlist(db.actor, npc, v.condlist)

if treasure_prob == "" or treasure_prob == nil then
treasure_prob = 0
end

if tonumber(treasure_prob) >= 0 and
v.community[npc:character_community()] == true and
v.active == false
then
if tonumber(treasure_prob) == 100 then
self:give_treasure(k)
else
table.insert(avail, {k = k, prob = treasure_prob})
tr_sum = tr_sum + treasure_prob
end
end
end
end

 


table.foreach(self.treasure_info, function(k,v)
if v.done then return end

local treasure_prob = xr_logic.pick_section_from_condlist(db.actor, npc, v.condlist)

if treasure_prob == "" or treasure_prob == nil then
treasure_prob = 0
end

if tonumber(treasure_prob) == 0 or not v.community[npc:character_community()] or v.active then
return
end

if tonumber(treasure_prob) == 100 then
self:give_treasure(k)
else
table.insert(avail, {k = k, prob = treasure_prob})
tr_sum = tr_sum + treasure_prob
end
end)

 

 

P.S. Использование функции table.foreach случайно обнаружил в файле .script распакованной геймдаты ТЧ

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

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


for name,v in pairs(self.treasures) do
if not (v.done and v.active) and v.community[npc:character_community()] then
local treasure_prob = tonumber(xr_logic.pick_section_from_condlist(db.actor, npc, v.cond)) or 0
if treasure_prob > 0 then --/ тайник может быть выдан
if treasure_prob < 100 then --/ заносим в таблицу возможных для выдачи
table.insert(avail, {k=name, prob=treasure_prob})
tr_sum = tr_sum + treasure_prob
else --/ безусловная выдача тайника
self:give_treasure(name, npc)
return --/> выходим из функции, т.к. выдаем только один раз!
end
end
end
end

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

Немного об оригинальном и "читабельном" вариантах:

1.

if treasure_prob == "" or treasure_prob == nil then

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

2. Оперировать с tonumber(treasure_prob), а в таблицу 'avail' заносить без преобразования. Уж или-или, т.е. если допускать что treasure_prob может не быть числом - то зачем же в дальнейшем для 'нечисла' допускать выполнение подобного: tr_sum = tr_sum + treasure_prob? Ведь очевидно же что будет ошибка в математике!

3. При 100%-ой вероятности - выдавать тайник ( self:give_treasure(k) ) и продолжать итерацию и дальнейшие проверки? Как будто с одного непися может быть получено более одного тайника?!

Ну и можно о других мелочах поговорить и посетовать, но и этого достаточно, чтобы не стОль на читабельность обращать внимание, как больше внимания уделять корректности и оптимальности кода.

ИМХО

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

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

Как раз в приведённых мною примерах я вообще не уделил внимание корректности и оптимальности, а только показал, как с помощью table.foreach можно избавиться от вложенности. Что конкретно происходит внутри цикла - я даже не вникал. Так что по пунктам 1,2 и 3 - это к GSC :)

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

Ложка дёгтя в бочку мёда...

Попытался вычислить время работы обоих итераторов в игре (ТЧ 1.0006 + X-Ray extension), вот результаты работы:

tbl = { ["a"] = 1, ["b"] = 2, ["c"] = 3, ["d"] = 4, ["e"] = 5, }
tbl2 = {}
tbl3 = {}

local timer = profile_timer()
timer:start()
for i = 1, 1000000 do
   for k,v in pairs(tbl) do
       if k~="b" then
           tbl2.k = v
       end
   end
end
timer:stop()
log1("##Time is "..timer:time()) --# 0.6806496875 sec

timer:start()
for i = 1, 1000000 do
   table.foreach (tbl, function (k,v)
           if k == "b" then return end
           tbl3.k = v
   end)
end
timer:stop()
log1("##Time is "..timer:time()) --# 1.581734375 sec

Как видно pairs выигрывает более чем в два раза. ;)

 

P.S. Поправил результаты. profile_timer считает время в микросекундах, а я делил результат на 105.

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

Проверил для ЗП 1.6.2 + X-Ray extension, результаты:

pairs: 0.162835 sec

foreach: 0.698536 sec

Достаточно убедительный довод, чтобы впредь не использовать foreach. Жаль конечно :(

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

Эх, будет мне наукою... Как оказалось метод start() класса profile_timer не обнуляет значение, а продолжает считать.

Вот ПРАВИЛЬНЫЕ результаты эксперимента:

--#------------------------------------------------------------
tbl = { ["a"] = 1, ["b"] = 2, ["c"] = 3, ["d"] = 4, ["e"] = 5, }
tbl2 = {}
tbl3 = {}
tbl4 = {}
--#------------------------------------------------------------
local timer1 = profile_timer()
timer1:start()
for i = 1, 1000000 do
   for k,v in pairs(tbl) do
    if k~="b" then
	    tbl2.k = v
    end
   end
end
timer1:stop()
log1("##Time is "..timer1:time()) --# 0.684159375 sec
--#------------------------------------------------------------
local timer2 = profile_timer()
timer2:start()
for i = 1, 1000000 do
   table.foreach (tbl, function (k,v)
	    if k == "b" then return end
	    tbl3.k = v
    end)
end
timer2:stop()
log1("##Time is "..timer2:time()) --# 0.9012660625 sec
--#------------------------------------------------------------
local function f(k,v)
   if k == "b" then return end
   tbl4.k = v
end
local timer3 = profile_timer()
timer3:start()
for i = 1, 1000000 do
   table.foreach (tbl, f)
end
timer3:stop()
log1("##Time is "..timer3:time()) --# 0.7032258125 sec
--#------------------------------------------------------------

И как видно, в эксперименте появился третий вариант (спасибо Charsi). Результаты на лицо.

Так что Shredder - приношу свои извинения за дезинформацию.

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

Когда я первый раз запускал эксперимент, то подумал об этом, и создавал три разных "счётчика", поэтому у меня результаты не изменились:

##time is 162232.109375

##time is 731144.5

##time is 566500.8125

 

PS: Результаты сильно разнятся, интересно, с чем это связано? У меня процессор Intel Core Duo T8100 2.10Ghz. А может из-за версии игры?

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

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

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

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

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

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

Войти

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

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

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