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

Язык 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
Ссылка на комментарий

Есть один нюанс языка, которому мало кто уделяет внимание. В 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

 

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

Доброго времени суток.

Наверное вопрос опять из разряда примитивных, но все же спрошу.

Возможно ли с помощью Lua отследить процесс (запущен или нет), а если запущен, то узнать состояние окна (развернуто или свернуто на панель задач (в трей).?

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

userbar368.png

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

Callisto,

это исключительно вопрос библиотек. В штатных библиотеках Lua, описанных в официальном мануале, таких возможностей вроде нет. Вообще говоря, Lua не больно силён в автономном варианте, и набор стандартных библиотек весьма беден. Подразумевается, что Lua - это всё же в основном расширение хост-программы.

 

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

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

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

 

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

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

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

Уважаемые знатоки!

Возможно, вопрос выеденного яйца не стоит, но всё же я его задам. Какой наилучший способ получения в 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

Понятно, что при однократном выполнении разница во времени несущественна. Однако для длинных циклов, может быть, какой-то метод является предпочтительным. Какой?

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

Kirgudu,

в случае Lua вряд ли удастся что-то оптимизировать. Если бы это был СИ, то я бы получил случайное 16-и или 32-х разрядное число один раз, а потом читал бы из него биты. В Lua же затраты на вызов самой функции генерации случайного числа сопоставимы, а может и больше, чем собственно генерация этого числа. Так что напрягаться смысла нет.

 

Artos,

навскидку я бы сказал, что вариант с плавающей запятой должен быть дороже, чем целочисленный. Однако, измерение показывает, что вариант math.random() в самом деле несколько быстрее, чем, скажем, math.random(2). Надо смотреть реализацию. Если второй вариант основан на первом, то это логично. Хотя вообще говоря должно быть наоборот, получение числа с плавающей запятой должно быть основано на генерации (псевдо)случайного целого.

  • Нравится 1
 

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

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

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

 

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

malandrinus, сам вопрос не однозначен, хотя вроде как и тривиален, и не стал пояснять свой пост, дав в начале просто 'затравку'.

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

Собственно для циклов тут три момента:

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

б) выбор метода для наиболее правильного вычисления случайного значения (все же метод math.random грешен в тех или иных условиях, т.е. случайность не всегда "случайна");

в) способ вычисления случайного значения с использованием math.random, т.е. использование тех или иных аргументов при вызове метода.

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

 

Вот в своем 'ИМХО' сделал упор именно на а) и в) применительно для циклов в игре, а про б) - не стал лезть в дебри. ;-)

Изменено пользователем Artos
  • Нравится 1

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

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

Artos, malandrinus, в своём примере я предполагал, что переменная num объявлена (и значение ей присвоено) за пределами цикла, а временные затраты интересуют именно на смену знака, а не на присвоение значения со знаком. Просто зря оставил этот момент за скобками.

Впрочем, теперь всё понятно. Честно говоря, я думал, что вызов math.random с параметрами быстрее, поэтому и выбирал между степенью минус единицы и сравнением с числом. Теперь степень отпадает, да и число для сравнения другое. Спасибо, буду знать.

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

 

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

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

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

 

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

 

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

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

Dennis_Chikin,

Предлагаю хотя бы в этом топике подбирать слова и выражать мысли по-возможности без субъективизма и навешивания шор.

 

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

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

 

"растет очень сильно" - то, что на перечисление бОльшего кол-ва аргументов/значений потребно бОльшее кол-во тактов/времени - это очевидно. А вот "сильно" и тем более "очень" в категоричной форме высказывать не стОит. Для кого-то пара микросекунд не играет никакой роли, а кому-то "сильно важны". Т.о. подобное - относительно!

 

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

 

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

 

ИМХО

 

 

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

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

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

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

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

 

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

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

 

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

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

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

 

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

(добавлю, чтобы развеять очередное заблуждение)

Скажем, я отказался от использования в 50 ms цикле актора чего-либо кроме "биорадара"/детектора артов.

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

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

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

(но это уже тема для скриптования иль ковырялок в скриптах)

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

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

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

Заинтересовал пост 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

Наверное так и понимать.

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

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

 

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 }

 

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

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

Наконец-то появился НЕТ.

Dennis_Chikin

Да мне-то по большому барабану как там устроено в Сталке.

Проповедую чистый lua.

И то, что я выяснил - будет в пользу мне же. Жалею что высказался тут. Непонимание - бич нашего времени.

Объясните мне, дураку, почему так получилось?

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

 

Это всё просто ради спортивного интереса.

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

Здравствуйте все, я начал изучать Lua, вы можете дать компилятор?

 

Держи: >>ClicK Me<<

ColR_iT

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

Объясните мне, дураку, почему так получилось?

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

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

 

Вообще-то уже давно пора посмотреть исходники 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);
}

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

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

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

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

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

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

Войти

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

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

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