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

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


Malandrinus

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

(изменено)

Artos, оффтопить, так офтопить...

Вот поддержу всеми конечностями. Когда скрипты той же НС всего-лишь после поверхностного касания худеют на четверть - это пол беды.

Даже месиво из того, что вчера разбирали (и даже если половина не работает) - тоже еще на так страшно по сравнению с гораздо худшим.

 

Во-первых, оно совмещалось вообще не думая. Например, могут быть подряд несколько попыток удалить один и тот же объект, разумеется, без проверки на его существование. Ну, заспавнено аддоном xStream аптечка, ну удаляется она по уходу непися в офлайн. Ну, нормально. Но так там еще 3 штуки таких, которые тоже удаляют. Причем существование аптечки запомнено еще при netspawn. Ага. Вот так вот прямо в виде серверного объекта... Результат понятен. Ну а что происходит потом с оффлайн_алайфом... В который тоже кусков понапихали. Этот и сам один и тот же предмет в цикле может пытаться удалять.

 

И ведь что-то поправить - надо перелопатить 2 десятка скриптов. А потом перекомпилируется олспавн, и снова лопатим скрипты, вбивая в них руками изменившиеся координаты и номера... Тоже по 2-м десяткам.

НУ и бесконечные простыни if ... elseif .... end'а не видно...

Бесконечные сканирования for i = 1, 65535 do obj = level.object_by_id(i) ... по нескольку штук в одном апдейте. iterate_inventory() туда-же...

 

Что до ООП и lua, то в данном конкретном случае вызовы вида obj:method() получились весьма неторопливыми, скажем так. И стремление все-все построить в виде якобы объектов оказывается крайне неполезным, если сравнить с результатом разворачивания этого творчества в самые обыкновенные функции.

 

Upd: просто некоторые инструменты могут быть опасны. Особенно, используемые по принципу "а по тому, что все так делают". К ООП это вполне относится.

 

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

Поделиться этим сообщением


Ссылка на сообщение

Вот интересно, почему local sim = alife() и далее по тексту - можно писать, а с vector() такое не прокатывает ?

Универсальное правило есть какое-то ?

 

Поделиться этим сообщением


Ссылка на сообщение

Ну, давайте еще раз поговорим об этом. ;) В смысле, о таблицах и добавлении/удалении записей в них.

Это я ведусь на провокацию.

 

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

 

Вообще говоря, ни кто не обещал какого-либо доступа к внтреннему счетчику, использующемуся при присваивании вида t = { v1, v2, ... }. Обещали только, что будет работать пара table.insert()/table.remove() и доступ вида v = t[n].

Тем не менее, когда операция t[n] = v делает внутри таблицы нечто непредсказуемое, а затем table.remove( t, n ) игнорирует наличие в таблице записи с индексом n, и удаляет фактически что попало - это вряд-ли можно считать как-то соотвествующим принципам гуманизма.

 

P.S. А вызов функций и передача параметров ( не говоря уже о вызове методов у "самодельных" объектов ) в текущей имплементации таки явно относятся к "тяжелым" операциям. Был бы оптимизатор, как практически во всех современных плюсях - разговор был бы совсем другой.

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

"Обрывок кода" - это и есть весь сколько-нибудь значимый код. nil в таблицу здесь попасть не может, нецифровых индексов или "дырок" случиться тоже не может.

В смысле, была надежда, что table.remove при однократном вызове таки индексы исправит как надо.

 

И говорить здесь имеет смысл именно про гуманизм. ;)

По той причине, что если у нас в результате получилась хэш-таблица, хочется ожидать, что # вернет 0 (ну или сколько у нас там осталось от индексированного, а table.remove() просто не будет делать с ней ничего. Иначе лучше ими действительно не пользоваться.

В действительности же предложенное решение с t1000s = #t1000 не спасает, поскольку до момента, когда крах таблицы становится очевидным (нет записи, которая должна быть, есть та, которой быть не должно), # возвращает ровно то же самое, что и посчитано руками. И даже хуже того, если считая руками, мы получим вылет по попытке что-то делать с уже несуществующим элементом, то с использованием # - не узнаем даже о самом факте краха. Собственно, пару лет назад так и случилось, когда под девизом "заоптимизируем все" поменяли на автомате все табличные функции на доступ через #t+1 / #t - 1.

 

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

 

Ну и для "солянок" очевидно то, что "непредвиденное" с любой нелокальной таблицей скорее произойдет, чем не произойдет. А в свете того, что удаление одиночного элемента - операция крайне редкая, таблицы же по большей части не великих размеров - я вот для себя вижу скорее причину отказаться именно от использования table.remove(), чем от чего-либо еще, и таблицы по необходимости пересоздавать. Тем более, что многие и сами по себе живут не долго.

 

 

P.S. Да, видимо, формулировка про "делает непредсказуемое" внесла неоднозначность. Читать следует как "внутри таблицы происходит нечто непредсказуемое при".

 

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

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Продолжаем разговор про таблицы.

 

Вот код:

function test_tbl()
    local t, s = {}, 0
    for i = 1, 10 do t[i] = i end
    table.remove( t, 10 )
    t[10] = 10
    table.remove( t, 10 )
    _util["list_tbl"]( t, "test_tbl" )
end

(list_tbl - там много всякого, но, в общем, классическое for k,v in pairs(t) do ... end)

 

Вот конечный результат:

1, value: 1

....

8, value: 8

10, value: 10

9 records in table [test_tbl] ( # возвращает 8, что логично. )

 

Промежуточные - 10(10), 9(9), 10(10)

 

То есть, либо table.insert()/table.remove() - кстати, in pairs()/next() не гарантируют последовательности 1, 2 ... - либо - все остальное.

 

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

Upd: да нет, я уже не сетую. Просто чтобы исключить другие причины. И, да, вот конкретно такой результат - он, все-таки, несколько удивляет. Скажем, я бы понял вылет с грохотом, или 1,2...9,10 (то есть, несработавшее вообще удаление).

 

Gun12, в смысле, код попробован на чистом lua, и конечный результат: 1, 2, ... 8, 9 ?

 

Upd2: проверил на меньших значениях: в сталкерском lua то же самое - рушится. Со SciTE, похоже, действительно, разница в реализациях. Ну и аминь. В принципе, имеет право - отрицать глупо, бессмысленно, и ничего не изменит.

 

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

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Artos, см. выше апдейт 2:

10 или не 10 - не влияет. Да хоть 3 всего. А вот для случая с одной - двумя записями - сейчас тоже проверю. Уже интересно стало.

 

Gun12, там в начале разговора было резюме: исходя из описания lua - не от ПЫС, а от создателей собственно языка, сталкеровкий lua на такую камасутру право, увы, имеет. (Хотя это, конечно, ближе к буквоедству - "делаем так, по тому, что явно не запрещено", чем к рациональному.)

 

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

Поделиться этим сообщением


Ссылка на сообщение

А подскажите кто-нибудь менее монстрообразную конструкцию для замены:

v1, v2, v3, v4 = s:match"(%-?[%-%d%.]*)[%s%,]*(%-?[%-%d%.]*)[%s%,]*(%-?[%-%d%.]*)[%s%,]*([%d%.]*)"

 

И, кстати, я, похоже, что-то не понимаю с поведением конструкций вида

 string.gfind( s, "[%w]*] ) и string.gfind( s, "[%w]+] ) 

Как оно должно работать ?

Поделиться этим сообщением


Ссылка на сообщение

По первому вопросу я хочу 4 числа без мусора из строки типа " -195.01,-5.12, 146.45, 50чего-то там ".

Все другие варианты, которые смог придумать, дают странное.

Чисел всегда 4 (ну или вообще 2), и именно по этой причине с итератором городить огород не хочется.

 

По второму вопросу: ну, там union, да. Для случая с просто %w смысла особого нет, но поведение всей конструкции от этого изменяться же не должно по сравнению со, скажем %w_ или %w%s ? Или должно ?

Если символ в строке не попал под шаблон, а мы просим 0 или больше - будет ведь все равно пустая строка ?

А если не менее одного - nil ?

Поделиться этим сообщением


Ссылка на сообщение

Дальше, зачем включать в символьный класс ещё и знак минуса "%-"?

Остался от экспериментов. В общем, переписал пока так:

(%-?[%d%.]+)%,%s* n раз без хвоста

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

Знак "*" по-любому делает возможным нахождение пустой строки в произвольном месте. Я кстати не уверен, что Lua это обрабатывает корректно.

Ага, похоже на то, что итератору крышу сносит. Причем вместо пустых строк начинает выдавать первое вхождение.

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Наверное, сюда все-таки. Ибо торможу. Не понимаю, КАК этот код работает в соответствии с комментариями. У меня наоборот получается. level_weathers.script:

local function is_time_between(t, t1, t2) -- returns true if t1 <= t <= t2 assuming 24 hour clock
return (t1 < t2 and (t >= t1 and t <= t2))
 or (t1 > t2 and (t >= t1 or t <= t2))
end

function WeatherManager:update_times(last_idx) -- update the sliding time window indexes
self.last_time_idx = last_idx
self.next_time_idx = last_idx % #self.trans_times + 1
self.last_time = self.trans_times[self.last_time_idx]
self.next_time = self.trans_times[self.next_time_idx]
end

-- adjust time indexes so that current time is within the window that is defined by these indexes
function WeatherManager:fix_times()
self.next_time_idx = 1
repeat
 self:update_times(self.next_time_idx)
until is_time_between(self.time, self.last_time, self.next_time)
end

То есть, repeat ... until должно выполняться ПОКА время в заданном интервале. Или нет ? Снимите меня кто-нибудь с ручника.

И, кстати, код производит впечатление сильно небезопасного, почему-то.

 

malandrinus, ага, спасибо. Это я просто странного прочитал, оказывается. А паскаль - он как-то мимо меня пролетел весь.

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

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

Поделиться этим сообщением


Ссылка на сообщение

По поводу оператора # по его поведению и скудному описанию языка можно предположить, что он возвращает значение некоего счетчика. А вот как и когда создается значение этого самого счетчика - очень похоже, что при первом требовании. То есть, первом применении собственно # или table.что-то-там()

Поделиться этим сообщением


Ссылка на сообщение
Как и ты заметил, в игре именно вызов без аргументов (=> результат с плавающей запятой) более быстрый (проверял не раз тестами на практике), чем с заданными целочисленными аргументами. Т.е. вероятно в движке компиллятор Lua все же именно math.random() исходен, а все остальное пляшет от него, приводя к накладным расходам.

 

Я как-то мерял время на function f() return end ... function f( a1, a2, ... an ), ну и соответственно давая на вход разное количество аргументов. Задумчивость по мере увеличения количества аргументов растет очень сильно. Аналогично и с возвращаемыми. ( Вызов метода у объекта - еще дороже - в разы. Видимо, в т.ч. из-за передачи/обращения к ссылки на сам объект. )

В общем, функции в несколько строк делать не надо однозначно - лучше уж "китайский код".

Возможно, не лишено смысла более интенсивное использование переменных с областью видимости на весь модуль.

 

Ну и, да, плавучка у ЛУА более "нативная", чем целочисленка.

 

P.S. Вот чего не понимаю, так это то, что обращение к переменным, записанное как v["id"] чуть не в разы быстрее, чем v.id.

Поделиться этим сообщением


Ссылка на сообщение

Во-первых, все же топик по языку Lua, а не по его интерпретации в Сталкере, поэтому в постах о тестировании следует обязательно указывать о "в чистом Lua" или в игре...

Во-вторых, не нужно выпячивать субъективные оценки без сравнительных критериев или пояснений.

 

Да, виноват. Это я про SoC. "Очень сильно" - это значит - сравнимо или даже больше, чем несколько-десяток строк с арифметикой/логикой, не оформленных в отдельную функцию. Ну, то есть, вынеся это в функцию - получаем выполнение в разы дольше. Вызывая не как f(), а как myobj:f() - опять же в разы.

Ну или как приведенный в тексте пример с function f() return end vs. function f( a1, a2, ... an ) return end - логично было бы ожидать разницу во времени на каждую переменную все-таки меньше, чем на собственно вызов.

 

Когда счет идет на миллисекунды (а именно на них и идет), то для 20-50 ms апдейта или вызова "пачкой" десятка-другого апдейтов - это критично. Скажем, я отказался от использования в 50 ms цикле актора чего-либо кроме "биорадара"/детектора артов. Не успевает.

Поделиться этим сообщением


Ссылка на сообщение

Это здесь обсуждалось какое-то время назад.

 

lua SoC вычисляет пользовательский размер таблицы при первом обращении, требующем этот самый размер. То есть, до оператора # или table.remove() его просто не существует (есть некие внутренние счетчики, на которые тонко намекает стандарт, и которые пользователю недоступны в принципе).

 

Размер вычисляется неизвестно как, но от 1 до t[n] ~= nil с шагом 1.

Повторно НЕ пересчитывается. table.insert()/table.remove() изменяют его сами как им заблагорассудится.

 

Кстати, при попытке применить table.remove() к несуществующему с точки зрения всех этих счетчиков и размеров элементу (даже если он на самом деле есть) приводит к странному (что тоже вполне разрешено стандартом)

 

Другие lua могут вести себя иначе (стандартом разрешено).

 

Резюме: либо все делать руками ( if t[k] == nil then n = n + 1 end ; t[k] = v и if t[k] ~= nil then n = n - 1 end; t[k] = nil ), либо только table.insert( t, v ) и if n <= #t then table.remove( t, n ) end.

Инициализация в этом последнем случае допустима как t = { 1, 2, 3 } но недопустима как t = { [1] = 1, [3] = 3 }

 

С обращением в цикле, соответственно, будут особенности. ;)

Поделиться этим сообщением


Ссылка на сообщение

Интересно, насколько неправильно я понимаю, что имея функцию вида function inc_a( a ) return a+1 end обращаться к ней как this.inc_a( a ) - бессмысленно чуть более, чем полностью ?

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

:не совсем понятно, о чём именно вопрос. Именно к такой функции бессмысленно?

Да, именно  такой. Или реальный пример:dialogs.script:

 

function is_wounded(first_speaker, second_speaker)
    if db.storage[first_speaker:id()].wounded ~= nil and
       db.storage[first_speaker:id()].wounded.wound_manager.can_use_medkit == true
    then
        return false
    end

    return xr_wounded.is_wounded(first_speaker)
end

function is_not_wounded(first_speaker, second_speaker)
    return not this.is_wounded(first_speaker, second_speaker)
end

 

 

 

На правильное использование похоже вотэто: class "motivator_binder" ( object_binder ) ...

:bind_object(this.motivator_binder(npc))

 

 

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

В смысле ? global name space ? функция в одном модуле, зовем из другого ? Изменено пользователем Dennis_Chikin

Поделиться этим сообщением


Ссылка на сообщение

Что-то я слегка не понял эту конструrцию:


if sDiv then sPattern = '[^%s%'..sDiv..']+'

..

for sValue in sStr:gmatch(sPattern) do table.insert(tRet, sValue) end

Если sDiv есть, то ЧТО у нас будет в таблице ?

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Смысл сего действа неясен. То есть, при sDiv == "," и строке ",,,,,,,,,," - получаем таблицу с 10 запятыми ? А из строки 12345,6789 получим таблицу с "12345," и "6789" ?

Да, иначе там else pattern = "[_%w]+" - это как раз понятно.

 

 

И вот еще тоже из той же серии. Здесь у меня вообще фантазия отказывает:

for k, v in string_gfind( s, "(.*)" ) do t[k] = v end

А это что должно делать ?

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

Поделиться этим сообщением


Ссылка на сообщение

Artos, приведенное - это amk ?

Ну, как бы, я бы не стал задавать вопрос про закомментированный кусок.

Очевидно, он был кем-то раскомментирован. Или взят откуда-то еще до того, как был закомменчен.

Поделиться этим сообщением


Ссылка на сообщение

При чтении ltx white_spaces любого вида по-моему вообще игнорируются. SOC по крайней мере.

Сколько чего в лог не выводил - всегда идет что-то типа v1,v2,v3.

 

И есть шанс, что для %s пробел и табуляция тоже идентичны.

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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

AMK-Team.ru

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