Dennis_Chikin 3 658 Опубликовано 4 Января 2015 Поделиться Опубликовано 4 Января 2015 (изменено) С чего начинать и где взять. Установка 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 Изменено 2 Марта 2015 пользователем Kirgudu Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Malandrinus 615 Опубликовано 8 Декабря 2012 Автор Поделиться Опубликовано 8 Декабря 2012 Есть один нюанс языка, которому мало кто уделяет внимание. В Lua, как в языке с сильным акцентом на использование ссылок, надо делать различие между понятиями "ссылка" и "значение". Переменная в Lua - это ссылка на значение. У ссылки как таковой нет типа, тип есть только у значения, на которое эта ссылка ссылается. Типы всем известны: число, строка, таблица, userdata, логическое значение и nil. Я обращаю внимание, что nil - это всего лишь одно из возможных значений, на которое может ссылаться переменная. Далее станет ясно, почему это важно. Имена даются ссылкам, индексированная позиция в массиве или ключ в ассоциированной таблице - это тоже ссылки. Если кто не знает, то обычные именованные переменные - это на самом деле тоже значения из специальных таблиц - отражающих область видимости. Это не так очевидно, но ссылки, вообще говоря, ведут себя так-же, как обычные стековые переменные, скажем, в СИ или в любом другом традиционном языке. Теперь что со сборкой мусора. Сборщиком мусора собираются значения, на которые больше нет ссылок. Возьмём обычную переменную внутри функции. function f() local a = "str" end Здесь вроде бы всё понятно, есть локальная переменная с именем "a" со значением "str". Мы говорим, что по выходу из функции переменная вышла за область существования и была удалена сборщиком мусора. При этом обычно не делается акцента на разделении "ссылка/значение". Однако, если рассматривать эту ситуацию более пристально, то происходит следующее. - По выходе из функции удаляется стек функции - Соответственно удаляется находящаяся в стеке ссылка с именем a - Это приводит к тому, что на значение "str" больше нет ссылок - Что в свою очередь приводит к тому, что это значение, где-то там находящееся, будет удалено сборщиком мусора. Чтобы иллюстрировать этот механизм, рассмотрим иную ситуацию. function f() local a = "str" a = nil a = 1 a = nil end Здесь мы: - завели локальную переменную (ссылку) с именем "a". - сразу присвоили ей значение "str" - затем присвоили её значение nil, что привело к тому, что на значение "str" ссылок больше нет и оно будет убрано сборщиком мусора когда-то. Но ссылка осталась! - и далее мы присваиваем этой-же ссылке другое значение, другого типа (на этот раз "number") - и затем опять присваиваем всё той-же ссылке значение nil, что приводит к тому, что на значение 1 ссылок больше нет, и оно будет убрано сборщиком мусора. - при выходе из функции ссылка "a" будет удалена, но, повторюсь, не сборщиком мусора, а вместе со стеком функции. Справедливости ради надо заметить, что значения типа "nil", "number" и "boolean" имеют фиксированный и малый размер, и поэтому хранятся прямо в ссылке, а не в динамической памяти. По этой причине их не требуется удалять сборщиком мусора, поскольку их удаление по сути осуществляется в ходе замены значения ссылки другим значением. Это всего-лишь оптимизация и на общей идее никак не сказывается. Вот ещё одна ситуация, где идея ссылка/значение становится более важной и заметной: a = "str" function f() local a for ... do a = 1 end print(a) end print(a) - имеется глобальная ссылка с именем "a", которая ссылается на строковое значение "str" - мы создали локальную переменную (ссылку, помните?) в стеке функции. Вопреки расхожему мнению, это не неинициализированная ссылка! Согласно правилам Lua она имеет значение nil. - в последующем блоке мы обращаемся к ссылке "a", но к какой именно? Вот здесь важно, что ранее мы создали ссылку с тем-же именем "a" внутри функции. Поскольку эта ссылка существует и имеет область видимости "ближе", нежели глобальная ссылка с тем же именем снаружи функции, то именно они и будет использована. - таким образом, именно локальная ссылка будет ссылаться на численное значение "1" - по выходе из функции локальная ссылка будет удалена вместе со значением "1", а глобальная останется и сохранит своё значение "str". Теперь, какое это всё отношение имеет к таблицам. В таблицах хранятся именно ссылки, а не значения. Поэтому, есть в общем разница между действием "удалить элемент таблицы" и "записать nil в элемент таблицы". Разница эта проявляется не всегда. Чтобы понять когда именно, надо к сожалению знать детали реализации языка. Таблицы имеют две части: часть "ассоциативный массив", т.е. с индексами общего вида, и часть "массив", т.е. "с целыми индексами, идущими без разрывов от единицы". Для ассоциативного массива в общем разницы нет, удаляем мы ссылку или пишем в неё nil. В самом деле, при взятии значения по ключу мы получим nil, как в том случае если мы ранее записали по этому ключу nil, так и в том, если такого ключа там никогда не было. Если подумать, то если даже ссылка там остаётся, то мы именно такой результат и получим - nil в любом случае. При переборе же циклом любого вида очевидно алгоритм попросту игнорирует ссылки со значением nil. Признаться, я не помню уже детали реализации, т.е. упаковывается ли там массив после записи nil по ключу или нет, но это и не важно. Вы всё равно нельзя стандартными средствами языка узнать реальный размер этой части таблицы. А вот где становится важно понятие о ссылках и значениях так это для части массива. Дело в том, что записывая nil по индексу массива мы делаем именно это, записываем в имеющуюся ссылку другое значение (а именно nil. Помните? Это всего лишь одно из возможных значений.). При этом с остальными ссылками ничего не происходит, никак массив не перепаковывается, индексы остаются на своих местах. Просто по одному из индексов сидит nil. К чему это приводит? Вот здесь и начинаются танцы с бубном. Создатели языка, видимо желая упростит описание, никак не акцентировали внимание на ссылочной природе происходящего. Из-за этого в ряде случаев имеется противоречивость. К примеру возьмём такой случай: t = {12,34,56} t[2] = nil for i,v in ipairs(t) do print(i,v) end for i,v in pairs(t) do print(i,v) end for i=1,#t do print(i, t[i]) end алгоритм перебора в цикле с использованием ipairs (первый вариант) перебирает до ближайшего nil, если идти с первого индекса, т.е. выведет только 12. Алгоритм перебора, с использованием pairs (второй вариант), выведет все значения "не nil", т.е. 12 и 56. Здесь пока всё работает в соответствии с правилами языка. А вот оператор # (взятие длины) будет возвращать длину массива без учёта дырки, и значит перебор массива по индексу (третий вариант) выдаст все три значения 12, nil, 56. Т.е. что происходит, есть часть таблицы "массив". Если я пишу в эту часть значение nil по одному из индексов, то это просто означает, что я записал nil, в одну из ссылок, хранящихся в этом массиве. Саму ссылку это действие не удаляет. Это влияет на те алгоритмы, которые в своей работе учитывают значения ссылок, а именно ipairs и pairs. А вот оператор взятия длины (#) очевидно значения по ссылкам в части таблицы "массив" не читает, а просто выдаёт его длину. Теперь ещё веселее. Возьмём такую ситуацию: t = {1,2,3} t[6] = 45 t[4] = 12 t[5] = 34 t[2] = nil print(#t) Этот код выведет значение 6, а вот такой: t = {1,2,3} t[6] = 45 t[4] = 12 t[5] = 34 t[4] = nil print(#t) выведет 3 Т.е. здесь мы имеем таблицу, где непрерывно идущие от единицы индексы имеют сложную историю формирования. В данном случае часть индексов 1-3 заведомо была создана как массив, в том время как остальные были добавлены вразнобой. Таким образом, если мы делаем дырку в первой части, то опертор длины её игнорирует, а если во второй, то учитывает. Было бы логично предположить, что оператор взятия длины сначала просто берёт длину части-массива, затем перебирает все элементы начиная со следующего и до дырки, однако вот такой код: t = {1,2,3} t[6] = 45 t[4] = 12 t[5] = 34 t[5] = nil print(#t) уже выдаёт 6, что в общем оставляет одни вопросы, как именно это там внутри работает, и какая оптимизация приводит к этому результату. К сожалению, эти вопросы нельзя игнорировать полностью, только слепо упираясь в официально озвученный синтаксис языка. Вот типичный пример, который практически важен, но несёт в себе именно это противоречие. Допустим, я передаю в функцию произвольное число аргументов. Я мог бы написать обработку этого случая так: function f(...) local args = {...} for i,arg in ipairs(args) do end end К сожалению, если среди аргументов встречается nil, что вполне может получиться на практике, то цикл перебора ipairs на этом остановится, и аргументы, идущие за nil, будут опущены. Однако, используя особенность оператора # игнорировать нулевые ссылки в массиве, я могу написать так: function f(...) local args = {...} for i=1,#args do local arg = args[i] end end Это позволит перебрать все аргументы, независимо от их значения. 3 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Callisto 2 Опубликовано 9 Декабря 2012 Поделиться Опубликовано 9 Декабря 2012 Доброго времени суток. Наверное вопрос опять из разряда примитивных, но все же спрошу. Возможно ли с помощью Lua отследить процесс (запущен или нет), а если запущен, то узнать состояние окна (развернуто или свернуто на панель задач (в трей).? Необходимо отслеживать несколько текстовых редакторов. Один способ у меня есть, но заинтересовала возможность реализации этого через Lua. Ссылка на комментарий
Malandrinus 615 Опубликовано 10 Декабря 2012 Автор Поделиться Опубликовано 10 Декабря 2012 Callisto, это исключительно вопрос библиотек. В штатных библиотеках Lua, описанных в официальном мануале, таких возможностей вроде нет. Вообще говоря, Lua не больно силён в автономном варианте, и набор стандартных библиотек весьма беден. Подразумевается, что Lua - это всё же в основном расширение хост-программы. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 11 Декабря 2012 Поделиться Опубликовано 11 Декабря 2012 По поводу оператора # по его поведению и скудному описанию языка можно предположить, что он возвращает значение некоего счетчика. А вот как и когда создается значение этого самого счетчика - очень похоже, что при первом требовании. То есть, первом применении собственно # или table.что-то-там() Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Kirgudu 1 217 Опубликовано 21 Декабря 2012 Поделиться Опубликовано 21 Декабря 2012 Уважаемые знатоки! Возможно, вопрос выеденного яйца не стоит, но всё же я его задам. Какой наилучший способ получения в Lua случайного знака числа? Навскидку в голову приходит это: local num = 10 num = num * math.pow(-1,math.random(1,2)) и это: local num = 10 if math.random(1,2) == 1 then num = -num end Понятно, что при однократном выполнении разница во времени несущественна. Однако для длинных циклов, может быть, какой-то метод является предпочтительным. Какой? Инструмент Ссылка на комментарий
Artos 99 Опубликовано 21 Декабря 2012 Поделиться Опубликовано 21 Декабря 2012 ИМХО: local num = (math.random() > 0.5 and 10) or -(10) "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 21 Декабря 2012 Автор Поделиться Опубликовано 21 Декабря 2012 Kirgudu, в случае Lua вряд ли удастся что-то оптимизировать. Если бы это был СИ, то я бы получил случайное 16-и или 32-х разрядное число один раз, а потом читал бы из него биты. В Lua же затраты на вызов самой функции генерации случайного числа сопоставимы, а может и больше, чем собственно генерация этого числа. Так что напрягаться смысла нет. Artos, навскидку я бы сказал, что вариант с плавающей запятой должен быть дороже, чем целочисленный. Однако, измерение показывает, что вариант math.random() в самом деле несколько быстрее, чем, скажем, math.random(2). Надо смотреть реализацию. Если второй вариант основан на первом, то это логично. Хотя вообще говоря должно быть наоборот, получение числа с плавающей запятой должно быть основано на генерации (псевдо)случайного целого. 1 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 21 Декабря 2012 Поделиться Опубликовано 21 Декабря 2012 (изменено) malandrinus, сам вопрос не однозначен, хотя вроде как и тривиален, и не стал пояснять свой пост, дав в начале просто 'затравку'. Собственно для однократного применения (вычисления), как сам Kirgudu в вопросе заметил, нет причин заморачиваться о вариациях. А от для циклов - он не дал намека на конструкцию цикла(ов). Собственно для циклов тут три момента: а) есть смысл непосредственно объявляя переменную, присваивать ей то или иное значение, а не за два этапа (вначале даем значение, а потом меняем знак, если ...); б) выбор метода для наиболее правильного вычисления случайного значения (все же метод math.random грешен в тех или иных условиях, т.е. случайность не всегда "случайна"); в) способ вычисления случайного значения с использованием math.random, т.е. использование тех или иных аргументов при вызове метода. Как и ты заметил, в игре именно вызов без аргументов (=> результат с плавающей запятой) более быстрый (проверял не раз тестами на практике), чем с заданными целочисленными аргументами. Т.е. вероятно в движке компиллятор Lua все же именно math.random() исходен, а все остальное пляшет от него, приводя к накладным расходам. Вот в своем 'ИМХО' сделал упор именно на а) и в) применительно для циклов в игре, а про б) - не стал лезть в дебри. ;-) Изменено 21 Декабря 2012 пользователем Artos 1 "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Kirgudu 1 217 Опубликовано 22 Декабря 2012 Поделиться Опубликовано 22 Декабря 2012 Artos, malandrinus, в своём примере я предполагал, что переменная num объявлена (и значение ей присвоено) за пределами цикла, а временные затраты интересуют именно на смену знака, а не на присвоение значения со знаком. Просто зря оставил этот момент за скобками. Впрочем, теперь всё понятно. Честно говоря, я думал, что вызов math.random с параметрами быстрее, поэтому и выбирал между степенью минус единицы и сравнением с числом. Теперь степень отпадает, да и число для сравнения другое. Спасибо, буду знать. Инструмент Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 23 Декабря 2012 Поделиться Опубликовано 23 Декабря 2012 Как и ты заметил, в игре именно вызов без аргументов (=> результат с плавающей запятой) более быстрый (проверял не раз тестами на практике), чем с заданными целочисленными аргументами. Т.е. вероятно в движке компиллятор Lua все же именно math.random() исходен, а все остальное пляшет от него, приводя к накладным расходам. Я как-то мерял время на function f() return end ... function f( a1, a2, ... an ), ну и соответственно давая на вход разное количество аргументов. Задумчивость по мере увеличения количества аргументов растет очень сильно. Аналогично и с возвращаемыми. ( Вызов метода у объекта - еще дороже - в разы. Видимо, в т.ч. из-за передачи/обращения к ссылки на сам объект. ) В общем, функции в несколько строк делать не надо однозначно - лучше уж "китайский код". Возможно, не лишено смысла более интенсивное использование переменных с областью видимости на весь модуль. Ну и, да, плавучка у ЛУА более "нативная", чем целочисленка. P.S. Вот чего не понимаю, так это то, что обращение к переменным, записанное как v["id"] чуть не в разы быстрее, чем v.id. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Shredder 49 Опубликовано 23 Декабря 2012 Поделиться Опубликовано 23 Декабря 2012 У меня в ЗП наоборот a = t.id выигрывает по скорости у a = t['id'], не в разы конечно, а процентов 10-15. Ссылка на комментарий
Artos 99 Опубликовано 23 Декабря 2012 Поделиться Опубликовано 23 Декабря 2012 (изменено) Dennis_Chikin, Предлагаю хотя бы в этом топике подбирать слова и выражать мысли по-возможности без субъективизма и навешивания шор. Во-первых, все же топик по языку Lua, а не по его интерпретации в Сталкере, поэтому в постах о тестировании следует обязательно указывать о "в чистом Lua" или в игре... Во-вторых, не нужно выпячивать субъективные оценки без сравнительных критериев или пояснений. "растет очень сильно" - то, что на перечисление бОльшего кол-ва аргументов/значений потребно бОльшее кол-во тактов/времени - это очевидно. А вот "сильно" и тем более "очень" в категоричной форме высказывать не стОит. Для кого-то пара микросекунд не играет никакой роли, а кому-то "сильно важны". Т.о. подобное - относительно! "функции в несколько строк делать не надо однозначно" - если далее все одно потребно обрабатывать именно "несколько строк", то это скорее дело вкуса по оформлению кода, и ни как не относится к производительности иль оптимиизации. Т.о. "однозначность" в таких фразах недопустима, как и вообще сама "рекомендация". Ну и наконец, выдирать некие куски строк из контекста всей конструкции функции/алгоритма и голословно говорить о "тормозах" иль "скорости" - некорректно и по сути бессмысленно в любом случае. Для знающего - это будет неинформативным мусором (отсутствуют цифры, критерии), для малоопытных - навешиванием очередных шор и рождение заблуждений. ИМХО Изменено 23 Декабря 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 23 Декабря 2012 Поделиться Опубликовано 23 Декабря 2012 Во-первых, все же топик по языку Lua, а не по его интерпретации в Сталкере, поэтому в постах о тестировании следует обязательно указывать о "в чистом Lua" или в игре... Во-вторых, не нужно выпячивать субъективные оценки без сравнительных критериев или пояснений. Да, виноват. Это я про SoC. "Очень сильно" - это значит - сравнимо или даже больше, чем несколько-десяток строк с арифметикой/логикой, не оформленных в отдельную функцию. Ну, то есть, вынеся это в функцию - получаем выполнение в разы дольше. Вызывая не как f(), а как myobj:f() - опять же в разы. Ну или как приведенный в тексте пример с function f() return end vs. function f( a1, a2, ... an ) return end - логично было бы ожидать разницу во времени на каждую переменную все-таки меньше, чем на собственно вызов. Когда счет идет на миллисекунды (а именно на них и идет), то для 20-50 ms апдейта или вызова "пачкой" десятка-другого апдейтов - это критично. Скажем, я отказался от использования в 50 ms цикле актора чего-либо кроме "биорадара"/детектора артов. Не успевает. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Malandrinus 615 Опубликовано 23 Декабря 2012 Автор Поделиться Опубликовано 23 Декабря 2012 Dennis_Chikin, Я как-то мерял время на function f() return end ... function f( a1, a2, ... an ), ну и соответственно давая на вход разное количество аргументов. Задумчивость по мере увеличения количества аргументов растет очень сильно. Аналогично и с возвращаемыми. ( Вызов метода у объекта - еще дороже - в разы. Видимо, в т.ч. из-за передачи/обращения к ссылки на сам объект. ) Это вполне объяснимо, если знать, как Lua обрабатывает аргументы и возвращаемые значения. И то и другое передаётся через стек языка, а помещение и извлечение значений соответственно выполняется внутренними функциями. Плюс к этому, если вызывается метод класса, организованный через luabind, то для его вызова используется предварительный поиск метода через оператор индексирования, прописанный в метатаблице. Этот оператор представляет собой достаточно тяжеловесный код, скомпилированный на С++. Поэтому на самом деле иногда имеет смысл не делать функцию, а просто вставить несколько лишних строк кода. Чистый код Lua без вызовов (т.е. голая арифметика) будет скомпилирован с помощью LuaJIT и в общем-то будет выполняться всего в несколько раз медленнее, чем код С/С++. С другой стороны, давать это в качестве общей рекомендации я бы не рискнул. Какой-нибудь новичок воспримет это как прямое одобрение использовать копипасту в качестве основного инструмента. В итоге, на фоне копеечной выгоды имеем лютый несопровождаемый ужас вместо кода. P.S. Вот чего не понимаю, так это то, что обращение к переменным, записанное как v["id"] чуть не в разы быстрее, чем v.id. Это не так и не может быть так, поскольку то и другое - синтаксически эквивалентные конструкции. Вот тест, который подтверждает сказанное. local t = {["asd"] = 123} local N = 1000000 local t1, t2, t3 for i = 1,2 do local t = profile_timer() t:start() for k=1,N do end t:stop() if i==2 then t1 = t:time() end end for i = 1,2 do local t = profile_timer() t:start() for k=1,N do local b = t["asd"] end t:stop() if i==2 then t2 = t:time() end --log3("t=%f", t:time()) end for i = 1,2 do local t = profile_timer() t:start() for k=1,N do local b = t.asd end t:stop() if i==2 then t3 = t:time() end --log3("t=%f", t:time()) end log3("t2/t1=%f", (t2-t1)/(t3-t1)) Я здесь пытался провести как можно более точные измерения: во первых, избавиться от эффекта Jit компиляции, для чего время берётся со второго цикла, во-вторых, убрать постоянную часть и оставить только время самой операции. Если запустить этот тест несколько раз, то видно, что отношение времён практически равно единице с небольшим случайным отклонением не больше пары процентов в обе стороны. Вот где есть разница, так это между индексацией строкой и числом. Причём, если значение берётся по индексу из "массивной" части, то это примерно в пять раз быстрее индексации строкой, но если ищем по ключу в хешированной части, то это уже примерно в 10 раз медленнее, чем индексация по строке. Если первое смотрится закономерно, то второе на самом деле удивительно. Значит, массив предпочтительней таблицы с ключами-строками, но строковые ключи предпочтительней, причем однозначно, произвольных целочисленных ключей! Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 23 Декабря 2012 Поделиться Опубликовано 23 Декабря 2012 (изменено) (добавлю, чтобы развеять очередное заблуждение) Скажем, я отказался от использования в 50 ms цикле актора чего-либо кроме "биорадара"/детектора артов. Предполагаю, что ты просто напросто вынес соотв.коды (привязав их к коллбэку!) из самого метода апдейта актора в сторонние скрипты, но суть то осталась той же(!), т.е. все одно все твои вынесенные функции работают в том же самом цикле актора. Чтобы вынести из цикла актора, требуется выносить в другие потоки - тогда про "освобождение" цикла актора можно что-то говорить... Да и все одно, практически вся игра (за частичным исключением графики/рендеров/...) синхронизирована на именно (суб)цикле актора и не имеет особого смысла для оптимизации одного субцикла размазывать нагрузку по другим (хотя бы и в др.потоках), работающим в том же самом основном цикле. Для оптимизации в первую очередь требуется не "территориально" разносить, а разносить во времени. (но это уже тема для скриптования иль ковырялок в скриптах) Изменено 23 Декабря 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Nazgool 250 Опубликовано 5 Января 2013 Поделиться Опубликовано 5 Января 2013 (изменено) Заинтересовал пост malandrinus-а по теме составления таблиц и оператора длины #. Провёл кое-какие исследования, и выяснил интересные вещи. Составил таблицу вида : t={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25} t[28] = 28 t[26] = 26 t[32] = 32 t[30] = 30 t[27] = 27 t[33] = 33 t[29] = 29 t[31] = 31 А теперь занилю некоторые значения : t[2] = nil t[4] = nil t[8] = nil t[16] = nil t[32] = nil И к чему я пришел? (использую оператор #) В показанном примере длина таблицы будет равна 1 Удаляемые поля в таблице до боли напоминают старшинство битов. Т.е. если удалить в вышеуказанной таблице только поле с индексом ("битом") 32, то оператор длины увидит всеполя до него. т.е. длина будет равна 31 Удалив поля с битами 32 и 16 (типа старшие биты в длине таблицы) оператор прочитает поля до меньшего(16-го бита), и длина будет равна 15. Интересно то, что для подобного эффекта нужно нилить поля с битами от старшего с меньшему по-порядку. t[4] = nil t[8] = nil t[16] = nil t[32] = nil покажет длину 3 t[8] = nil t[16] = nil t[32] = nil покажет длину 7 Если заниление будет происходить не по порядку, то длина определяется до старшего бита. t[8] = nil t[32] = nil определит длину 31 Удаление полей с другими битами ни к чему подобному не приводит. Ну или пока не докопался. Хотя...Нашел ещё такое t[32] = nil t[30] = nil длина 29 t[32] = nil t[28] = nil 27 t[32] = nil t[24] = nil 23 t[32] = nil t[16] = nil 15 Зависимость похожа, только как это понять? В обратную сторону тоже работает? Не пользоваться одновременно заниливанием и оператором #. Есть table.remove Наверное так и понимать. Изменено 5 Января 2013 пользователем Gun12 1 Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 5 Января 2013 Поделиться Опубликовано 5 Января 2013 Это здесь обсуждалось какое-то время назад. 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 } С обращением в цикле, соответственно, будут особенности. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Nazgool 250 Опубликовано 14 Января 2013 Поделиться Опубликовано 14 Января 2013 (изменено) Наконец-то появился НЕТ. Dennis_Chikin Да мне-то по большому барабану как там устроено в Сталке. Проповедую чистый lua. И то, что я выяснил - будет в пользу мне же. Жалею что высказался тут. Непонимание - бич нашего времени. Объясните мне, дураку, почему так получилось? Ведь закономерность в моих исследованиях очевидна?! Это всё просто ради спортивного интереса. Изменено 14 Января 2013 пользователем Gun12 Ссылка на комментарий
Вячеслав Якубчик 0 Опубликовано 28 Января 2013 Поделиться Опубликовано 28 Января 2013 (изменено) Здравствуйте все, я начал изучать Lua, вы можете дать компилятор? Держи: >>ClicK Me<< ColR_iT Изменено 28 Января 2013 пользователем ColR_iT Ссылка на комментарий
abramcumner 1 159 Опубликовано 29 Января 2013 Поделиться Опубликовано 29 Января 2013 (изменено) Объясните мне, дураку, почему так получилось? Ведь закономерность в моих исследованиях очевидна?! А так похоже на криво реализованный бинарный поиск. А может так и хотели, следуя методике: неправильные данные на входе, неправильные данные на выходе. Нечего путать операции между собой. Вообще-то уже давно пора посмотреть исходники lua и закрыть этот вопрос с таблицами. Так и есть /* ** Try to find a boundary in table `t'. A `boundary' is an integer index ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). */ int luaH_getn (Table *t) { unsigned int j = t->sizearray; if (j > 0 && ttisnil(&t->array[j - 1])) { /* there is a boundary in the array part: (binary) search for it */ unsigned int i = 0; while (j - i > 1) { unsigned int m = (i+j)/2; if (ttisnil(&t->array[m - 1])) j = m; else i = m; } return i; } /* else must find a boundary in hash part */ else if (isdummy(t->node)) /* hash part is empty? */ return j; /* that is easy... */ else return unbound_search(t, j); } Изменено 29 Января 2013 пользователем abramcumner Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти