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

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

Внезапно рухнула сеть, поэтому не отписывался.
Описание теста: платформа ТЧ 1.0006(топология), встроено расширение луа от артоса(by RvP), вернул родной xrLua - результаты теста теже. (Заодно проверил на 4 патче(+от солянки) с чистой геймдатой) - также вставляет не в 1ю позицию.)

local tt = {1,2,3,4,5,6,7,8,9,10,11} -- таблица
local s
my_print_table("начинаем тест", tt)
table.insert(tt, "test1") -- первое обращение к таблице, счетчик создан
for i = 1, #tt do tt[i] = nil end -- удалили поля
-- table.remove(tt, 1)
my_print_table("пустая таблица", tt) -- видим пустую таблицу
table.insert(tt, "test2") -- вставили поля, у меня они встают в 11, 12 ячейке соотв.
table.insert(tt, "test22")
-- table.remove(tt, 1)
my_print_table("добавили 2 поля", tt)
table.insert(tt, "test3")
table.remove(tt, 1)
my_print_table("еще одно поле, сместили все поля", tt) -- все поля встают где-то в районе 9-14 индекса
table.remove(tt, 1)
my_print_table("еще сместили все поля", tt)
-- for i = 5, 11 do tt[i] = i end
table.remove(tt, 1)
table.remove(tt, 1)
my_print_table("fase 7", tt)
-- for i = 1, 11 do tt[i] = nil end
-- for i = 1, 11 do tt[i] = i end
table.insert(tt, 4, "index")
-- table.insert(tt, "index2")
my_print_table("добавляем поле в середину таблицы, конец теста", tt)
Функция вывода таблицы
function my_print_table(text, tt)
qqq(text)
s = "parse tt= " for i = 1, 20 do s = s..string.format("[%i]=%s, ", i, tostring(tt[i])) end qqq(s)
end


Урезанный лог, удалил nil-ы до интересующих ячеек и после

начинаем тест
parse tt= [1]=1, [2]=2, [3]=3, [4]=4, [5]=5, [6]=6, [7]=7, [8]=8, [9]=9, [10]=10, [11]=11, [12]=nil
пустая таблица
parse tt= [9]=nil, [10]=nil, [11]=nil, [12]=nil, [13]=nil, [14]=nil,
добавили 2 поля
parse tt= [9]=nil, [10]=nil, [11]=nil, [12]=nil, [13]=test2, [14]=test22,
еще одно поле, сместили все поля
parse tt= [9]=nil, [10]=nil, [11]=nil, [12]=test2, [13]=test22, [14]=test3,
еще сместили все поля
parse tt= [9]=nil, [10]=nil, [11]=test2, [12]=test22, [13]=test3, [14]=nil,
fase 7
parse tt= [9]=test2, [10]=test22, [11]=test3, [12]=nil, [13]=nil, [14]=nil,
добавляем поле в середину таблицы, конец теста
parse tt= [4]=index, [5]=nil, [6]=nil, [7]=nil, [8]=nil, [9]=nil, [10]=test2, [11]=test22, [12]=test3
test_4 started from 11311.940429688


Как видим - или у меня такой особый сталкер, или повторные table.insert/remove напрочь игнорируют изменения таблицы. Напоминаю - таблица была занилена после первого их использования.

 

как это игнорируют? Нормально они работают, в т.ч. и на 6 патче, все обновляется, и оператор, и значения.

Возможно я не так выразился, имеется ввиду, что позиция по умолчанию для table.insert/remove в сталкере не обновляется, если ранее они были вызваны для заполненной индексом таблицы. Т.е. берем массив, что-то делаем с ним через вставку/удаление, разрушаем массив nil-ми.. следующие вставки/удаления проходят по старой позиции, хотя перед ней может быть и 100500 пустых. Фактически, я не вижу в своем тесте, чтобы каждая новая вставка/удаление учитывали текущую длину массива.
Изменено пользователем Xdlic
Ссылка на комментарий

При работе с таблицей следует придерживаться одного из двух стилей:

- только как с обычным массивом, используя table.insert\remove и т.д.

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

Смешивать их не стоит. В SciTE с Lua 5.1 проблемы нет. А современный Lua - версии 5.3 - ругается на вставку элемента  с помощью table.insert за пределы границ - bad argument #2 to 'insert' (position out of bounds).

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

@Xdlic

Да, иногда в сталкере стандартные действия Lua ведут себя совсем не так.
Когда то меня чуть не предали анафеме за мои высказывания в сторону ненормальности сталкеровского Lua. Но уже который раз это подтверждается :)
Что там сделали с Lua я не знаю, но ...
Значение "nil" хоть и обозначает отсутствие значения, всё-таки это одно из значений Lua, в отличии от отсутствия вообще какого-либо значения.
Возьми к примеру функции :
function f()
    return
end
и
function f()
    return nil
end
Первая не возвращает ничего, а вторая возвращает значение. nil ведь тоже значение.
Видимо в сталкере разрабы как-то акцентировали на этом внимание, поэтому такие результаты.
В любом случае этот вопрос решается просто.
Ты используешь :
for i = 1, #tt do tt[i] = nil end
На самом деле ты присваиваешь полям значения (nil).
А нужно удалять поля :
for i = #tt, 1, -1 do
    table.remove(tt, i)
end
Изменено пользователем Nazgool
  • Согласен 2
Ссылка на комментарий

 

 

Значение "nil" хоть и обозначает отсутствие значения, всё-таки это одно из значений Lua, в отличии от отсутствия вообще какого-либо значения.

Прости?

Очень интересно высказывание, почему ты так решил?

Freedom

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

@_Призрак_

function f()
    return
end

print(f()) --> ничего
print(type(f())) --> bad argument #1 to 'type' (value expected) т.е. Lua значения нет

function f()
    return nil
end

print(f()) --> nil
print(type(f())) --> nil -- а тут значение есть. nil ведь одно из 8-ми значений Lua
Изменено пользователем Nazgool
Ссылка на комментарий
А нужно удалять поля

Давайте оставим этот момент в стороне. По отношению к индексным массива в сталкере вообще никак иначе обращаться категорически не рекомендую(как раз из за необновляющегося счетчика).

Собственно я отправил свое сообщение с мыслью, что получается в движке сталкера функции добавки/удаления реагируют на изменения таблицы совсем не так, как функция #t.

Вопрос имел целью узнать, как влиять на выбор текущей позиции в этих самых table.insert/remove.

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

Пример обертки, которая корректирует значение позиции текущего конца индекса



function insert_two(tab, pos_or_val, val)
    if val then
        table.insert(tab, pos_or_val, val)
    else
        table.insert(tab, #tab+1, val)
    end
end

Но это не отменяет того факта, что выйдя за пределы первоначального диапазона мы получим результат, когда начало таблицы наползает на ее конец

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

@Xdlic

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

table.insert(tt, #tt+1, "value")

#tt как раз и определит длину массива, а +1 поставит значение в конец.

А если делать обертку, то так, чтобы не навредить  вызовам table.insert в других скриптах

do
    local old_insert = table.insert
    function table.insert(t,p,v)
        old_insert(t, v and p or #t+1, v or p)
    end
end
Изменено пользователем Nazgool
Ссылка на комментарий

Собственно я отправил свое сообщение с мыслью, что получается в движке сталкера функции добавки/удаления реагируют на изменения таблицы совсем не так, как функция #t.

А почему они должны реагировать так же, как #t?
Ссылка на комментарий

А почему они должны реагировать так же, как #t?

Хотя бы затем, что имено если бы они реагировали также как #t, то это позволяло бы работать в сталкере с концом строки по упрощенному варианту t[#t+1] = val и t[#t] = nil вместо вызовов функций. В чистом луа как раз это и работает.

Никто не исследовал вопрос, где хранятся эти созданные счетчики? В недоступной части таблиц или где-то внутри движка Lua/Сталкера?

Upd:

insert работает абсолютно так же.

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

Ну хорошо, раз в сталкере нельзя работать с индексами по упрощенному варианту, значит будем каждый раз вызывать соответстующие функции... а если одновременно нужно этими инсертами забить 100500 значений в таблицу, которые заранее нельзя задать? Мы получим ощутимый нагруз от этих 100500 вызовов функции table.insert(ну ладно, пусть еще можно ссылку хранить локально). Или в этом случае полностью придется отказаться от индексных массивов и получать случайный перебор элементов из pairs().

Upd2:

И да, смысла в предложенной обертке меньше, чем 0. Чтобы она работала, нужно в ней получать доступ к счетчику и вручную его менять.

Upd3:

Внезапно работает. 

Видимо я и вправду не умеею внятно изъяснять свои мысли/идеи. Твой вариант уже не будет работать совместно с table.insert/remove. Стоит их один раз вызвать и счетчик перестает зависеть от структуры таблицы. Про индексный массив с 100500 строк, тогда уж проще сделать обертку не вокруг самих функций, а вокруг метода. Т.е. реализуем свои table.insert_new/remove_new

function table_insert_new(t, pos_or_val, val)
    local end_pos = #t
    if val then
        if pos_or_val < end_pos then
            for i = end_pos, pos_or_val, -1 do
                t[i] = t[i-1]
            end
        else
            t[pos_or_val] = val
        end
    else
        t[end_pos+1] = val
    end
end

И подобное для remove, вроде алгоритм правильно описал

В результате не будет этого статического счетчика позиции для добавления/удаления, который я вижу в сталкере. А самое главное - можно будет спокойно работать как с t[#t+1] = val, так и с новой версией вставки значений в СЕРЕДИНУ индексного массива.

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

@Xdlic, воистину ты занимаешься фигней, insert вставляет значение в nil или в конец, а #t получает количество элементов в таблице тоже по своим условиям, в зависимости есть nil в конце или нет, в итоге мы видим что твоя обертка вообще не несет никакой смысловой нагрузки, если на конце будет nil, то твоя обертка вставит значение вместо первого попавшегося nil, insert работает абсолютно так же.
 


это позволяло бы работать в сталкере с концом строки по упрощенному варианту t[#t+1] = val и t[#t] = nil вместо вызовов функций

Внезапно работает. 
local a = {}
a[#a+1] = 1
log(a[1])

 

 

нигде недоступен

можешь рубить последний элемент, потом работать, потом его назад вставлять.


я у себя с этим боролся с помощью pairs, вполне нормально выглядит.

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

@Xdlic, воистину ты занимаешься фигней.

Будет время - посмотрю, что там внутри этих таблиц, вдруг счетчик хранится в метаданных самой таблицы.

Ладно, оставим в покое этот болезненный вопрос урезанного сталкерского Lua, интересно, какие еще особенности языка  ПЫСы сумели сломать к релизу?

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

Хотя бы затем, что имено если бы они реагировали также как #t, то это позволяло бы работать в сталкере с концом строки по упрощенному варианту t[#t+1] = val и t[#t] = nil вместо вызовов функций.

Ну и работай так сейчас. Что мешает?

 

В чистом луа как раз это и работает.

Что такое чистое луа?

@Xdlic, попробуй для получения "внутреннего счетчика" использовать table.getn(t).

  • Спасибо 1
Ссылка на комментарий

попробуй для получения "внутреннего счетчика" использовать table.getn(t).

 

    Не обратил ранее на эту функцию внимания, считая ее аналогичной #. Проверил заодно и table.setn - да, эти функции как раз и позволяют получать и задавать "счетчик" с которым работают функции вставки/удаления. Можно ручками забивать строки через t[#t+1] = val, а затем обновлять его table.setn(t, #t) и стандартные insert/remove будут "видеть", что с таблицей что-то случилось :).. и соответственно будут обрабатывать также и новые данные.

Но... зачем-то их ведь удалили из стандартного набора Lua-функций. В SciTe-ru пишет: 'setn' is obsolete.

 

P.S. Споры о том, можно ли так делать или это "моветон" пропущу мимо ушей.

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

Но... зачем-то их ведь удалили из стандартного набора Lua-функций. В SciTe-ru пишет: 'setn' is obsolete.

Потому что table.insert/remove стали работать через #t. Но оператор решетка не всегда был в Луа.
Ссылка на комментарий

Столкнулся с необходимостью сравнить две переменные типа userdata (копаю gui классы из луа и там возникла в этом потребность) . Но меня ждал Великий Облом в виде ошибки:

 

LUA error: No such operator defined
В начале вылетел при проверке на неравенство, потом та же ошибка и при проверке на равенство...

Выдержка из руководства: "...и не имеет предопределенных операций в Lua, за исключением присваивания и проверки на равенство."

 

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

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

Столкнулся с необходимостью сравнить две переменные типа userdata

В сталкере юзердаты так нельзя сравнивать.

 

копаю gui классы из луа и там возникла в этом потребность

Не уверен, что именно это тебе нужно, но попробуй нечто вроде tostring(CUIStatic) == tostring(CUIStatic). Изменено пользователем Shadows
Ссылка на комментарий

@Xdlic

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

Но это легко изменить, используя расширение lua.

Прежде всего стоит определить утверждения, касающиеся lua в игре, которые и помогут в решении.

 

1. Для типа 'userdata' установлена единая метатаблица.

    P.S. Категорично утверждать не буду, не все проверял, но например для UI окон и для

    Волка она едина.

    Это позволяет предположить, что если для таких различных объектов метатаблица едина,

    то она едина и для всех остальных.

 

2. Метаметод '__eq' вызывается только при сравнении двух юзердат (тот, что не работает).

    Сравнение 'userdata' == 'другой тип данных' обрабатывается обычным образом.

 

3. Поскольку при сравнении двух типов 'userdata' вызывается метаметод '__eq', то результат

    сравнения нужно произвести без вызова метаметода. Функция 'rawequal' именно это

    и делает.

 

Отсюда следует, что можно переопределить метаметод '__eq', даже не сохраняя ссылку на

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

local userdata_mt = debug.getmetatable(CUIStatic())

function userdata_mt.__eq (a,
    return rawequal(a,
end

Вместо "CUIStatic()" можно использовать любое получение 'userdata'.

Ну и наконец вызов этого кода необходимо поставить на 'net_spawn', т.к. после

каждой загрузки новые данные поля 'userdata_mt.__eq' будут зачищаться.

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

Отсюда следует, что можно переопределить

метаметод '__eq', даже не сохраняя ссылку на

прежний, т.к. этот метод больше ни для чего не

нужен, и ничего не затрагивает.

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

 

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

Lua 9/10 времени занимается передачей управления внутрь метаметода и 1/10 - подстановка значения в выражение.

 

Скажу одно - создание в сталкере классов "что было объектно-ориентированым" неразумно. К примеру тот же менеджер событий OGSE - получение экземляра обернуто в дополнительную функцию.. лишь для того, чтобы не плодить новые экземпляры. Ну так сделали бы на базе таблицы: интерфейс "как у класса", а что таблица - все равно в обертку завернуто. Классы здесь, как чесать правое ухо левой пяткой. Повторюсь, без мало-мальской реализации автоподстановки кода классы еще хуже создания пользовательских метаметодов - производительность работы с данными падает в десятки раз.

 

Меня окончательно добило, когда. цикл обхода значений внутри класса по производительности оказался в 50-60 раз медленнее, чем из глобальной таблицы.

 

 

Проще взять за практику сравнивать юзердаты raw'вом, чем что-то прикручивать на локальном уровне. Оба поста скорее констатация факта;), но разумная критика приветствуется.

 

И да, сравнение юзердат оказалось несколько излишним. При достаточной синхронизации учасков кода оно совершенно не нужно.

Upd пропустил пост

Не уверен, что именно это тебе нужно, но

попробуй нечто вроде tostring(CUIStatic) ==

tostring(CUIStatic).

И будет внутридвижковое нефатальное исключение, по просту зависнет. У меня tostring nil'ы жрет, а юзердатами давится
Изменено пользователем Xdlic
  • Нравится 1
Ссылка на комментарий

@Xdlic

Вы наверное не полностью поняли о чем я писал.
 
Во-первых скажу, что если Вы ничего не будете делать, т.е. оставите всё как есть,
то это не отменит вызова стандартного метаметода при сравнении двух юзердат ))
 
Во-вторых вызов этого метаметода не приводит к результату. Вернее приводит к
результату печальному.
 
В-третьих. Раз метаметод вызывается, то предположительно внутри что-то присходит. И на это
затрачивается некое кол-во шагов алгоритма. Так почему не сделать правильно, т.е. выбросить
прежние "накладные расходы", как Вы говорите, и не заменить своими.
Вы должны были заметить, что это не обертка для прежнего "__eq", это новый метод "__eq".
 
В-четвертых, каким образом код станет зависимым от этой правки, если этот метаметод,
повторю, вызывается только! при сравнении двух юзердат, и более того - результат его работы плачевен.
Что мы теряем?
 
Ну и в-пятых пользуемся привычным сравнением "==" "~=" не задумываясь, - "А не юзердаты ли
сравниваемые переменные?"
Изменено пользователем Nazgool
Ссылка на комментарий

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

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

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

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

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

Войти

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

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

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

AMK-Team.ru

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