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

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

Вот не хотел же влезать :) Всё это надо читать в документации, а не хватать по форумам.

Луа:

2.1

В Луа 8 типов: nil, boolean, number, string, function, userdata, thread, and table

Из них 4 типа: nil, boolean, number, string - являются типами-значениями. Это значит, например, что при передаче в функцию, передается их копия.

function f(a)
  a = 5
end
local x = 7
f(x)
print(x) -- выведет 7, потому что в функцию передалась копия
Оставшиеся 4 типа: function, userdata, thread, and table в документации луа названы "objects" - я бы сказал по-русски ссылочный тип. Перменные таких типов хранят не сами значения, а ссылки на них.

function g(a)
  a.y = 5
end
local x = {y = 7}
g(x)
print(x.y) -- выведет 5, потому что в функцию передалась ссылка и по этой ссылки значения можно менять.
Если программист до луа изучал си , то все ссылочные типы можно воспринимать как константные указатели, чем они на самом деле и являются. Правда константных указателей в C вроде не было, они только в C++ появились :)
Ссылка на комментарий

Опс... Что-то про string я захотел посмотреть код. Точнее, много кода. Если это действительно так, то я перестаю понимать, как работает многое из того, что работает. Ну вот в частности вообще все таблицы.

Замени в примерах выше числа на строки: 5 на "aa", 7 на "bb". Ничего не изменится.

Приводи код, который непонятно работает.

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

выведет 7, потому что в функцию передалась копия

Да, но почему этот пример такой? Вот сразу же дальше для ссылочных типов ты верно все пишешь, но если использовать функцию f(a) для них, тоже магии не произойдет, потому что ты переопределяешь аргумент, наружу это никак не уйдет в любом случае, будь там хоть value type хоть reference type.

как-бы в сталкере они связаны

Это не дает нам права разводить откровенный оффтоп. Тут C++, и он закончился в начале этой страницы, но я надеюсь тред будет перенесен в тему Lua.
  • Согласен 1

ТЧ 1.0004. SAP и Trans mod

github

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

Да, но почему этот пример такой? Вот сразу же дальше для ссылочных типов ты верно все пишешь, но если использовать функцию f(a) для них, тоже магии не произойдет, потому что ты переопределяешь аргумент, наружу это никак не уйдет в любом случае, будь там хоть value type хоть reference type.

Не понял. Приведи другой пример... Я просто показал, что в луа есть типы-значения и ссылочные типы и чем они отличаются. Ну и у ссылочных типов в луа семантика указателей.
Ссылка на комментарий

local test_s1 = ""

local test_s2 = ""

local test_i = 0

 

function test( v ) end

 

for i = 1, 10000 do test_s1 = test_s1 .. " " end

 

local pt = profile_timer()

pt:start()

for i = 1, 10000 do test ( test_s1 ) end

pt:stop()

log( "info", "string 200000: %s", pt:time() )

 

local pt = profile_timer()

pt:start()

for i = 1, 10000 do test ( test_s2 ) end

pt:stop()

log( "info", "string 0: %s", pt:time() )

 

local pt = profile_timer()

pt:start()

for i = 1, 10000 do test ( test_i ) end

pt:stop()

log( "info", "int: %s", pt:time() )

 

 

string 200000: 30992.1328125

string 0: 231.48368835449

int: 216.08390808105

 

Таки да, действительно копирует. Прощай очередной "оптимизаторский" миф про "строки существуют только в одном экземпляре".

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

Не понял. Приведи другой пример...

Показать, что value type передаются по значению? У меня только 1 вариант приходит в голову, это надо получить их адреса и показать, что они разные. Но я не знаю как это сделать :)print variable address lua

 

@Dennis_Chikin, омг, да ты о чем? Сам же приплетаешь способы передачи аргументов и сборщик, это к чему вообще? Есть 2 типа переменных - value type и reference type. Первый при передачи в функцию копируется, второй нет. У value type адреса будут отличаться, у reference type нет. Все. Точка. Никаких присваиваний, созданий переменных и прочей лабуды.

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

ТЧ 1.0004. SAP и Trans mod

github

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

2 Desertir: только код смотреть. Адреса же нам принципиально не показывают в явном виде.

 

Собственно, имея в виду (господи, как же это и назвать-то теперь, чтоб очередной страницы флуда не получить) - ну, вы поняли - я бы вообще сделал банально:

 

Если что-то передается в функцию - передается адрес (у нас внутри все равно не регистровые операции, а если и регистровые, то с предварительной загрузкой из стека). То есть, пока нет присваивания - по этому адресу и получаем. Как только присваивание - создаем новую переменную, и указатель выставляем на нее.

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

 

 

2 abramcumner: ожидаемо, 246 для таблицы (vs 212 и 212 для "" и 0 ).

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

Вопрос такой. Есть метод, перебирающий таблицу объектов и вызывающий у каждого объекта метод inject. Сам метод, в зависимости от ситуации может быть как собственно методом, так и переопределенной внешней функцией. Как, кроме объявления этих объектов в глобальной переменной, можно получить доступ к self внутри метода(непереопределенного)? Возможно мне нужно что-то про замыкания, но я в них толком не разобрался

Этот метод также вызывает эту функцию. Т.е. мне нужно либо вызывать функцию внутри метода, либо вместо метода.

 






for k, v in pairs(self.elements) do

oActive_elem = v

  -- v:inject(...) -- если это метод, то вызываем его как метод

  v.inject(...) -- если это переопределенная функция, то передаем параметры как есть

end

 

 

 

Нужно, чтобы если поле объекта - метод, то можно было получить внутри метода доступ к self. А если поле объекта - функция, то избежать получения первым аргументом ссылки self на этот объект.

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

@Xdlic, весьма странное поведение. Объясни что ты хочешь сделать, у меня чувство, что ты выбрал не совсем верную стратегию.

И еще, ты используешь LuaBind?

ТЧ 1.0004. SAP и Trans mod

github

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

Хочу реализовать в сталкере классы, в которых методы будут менять свое поведение в зависимости от параметров объекта(замещаться аналогичными методами).

Пока это все в виде набросков, но схема предложенного выше такова:

Есть класс (скажем 1), задачей которого являет обновление состояния массива объектов другого класса (2). В этом втором классе обновление может быть реализовано как в виде метода (при инициализации в метод inject подставляется нужный в данной ситуации метод), так и в виде функции (этот метод предназначен, в свою очередь для вызова сторонней функции).

Можно конечно не заморачиваться и просто использовать обертку вида

function element_class:inject(...)
  self.fun(...)
end

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

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

Определение метода и его вызов, это просто сахар в Луа.

local some_object = { one = 1, two = 2 }

--определили метод с "сахаром"
function some_object:print_one()
    print("Print one: "..self.one)
end

--определили метод без "сахара"
some_object.print_two = function(self)
    print("Print two: "..self.two)
end

some_object:print_one() --Print one: 1
some_object:print_two() --Print two: 2

some_object.print_one(some_object) --Print one: 1
some_object.print_two(some_object) --Print two: 2

--если так сильно надо заменить метод и чтобы он оставался методом, можно сделать так
function print_three(self)
    print("Print three: "..self.three)
end

some_object.three = 3
some_object.print_two = print_three --пошалим

some_object:print_two() --Print three: 3
some_object.print_two(some_object) --Print three: 3
Так можно сделать, но это скорее всего приведет к багам или "непредвиденным последствиям" (с). Для изменения поведения есть условные ветвления.

ТЧ 1.0004. SAP и Trans mod

github

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

@Xdlic, используй метатаблицы, в зависимости от условий переопределяешь метод. Для независимых таблиц подходит без всякого странного, с движком так уже не получится, там пока никак не получается (и не только у меня). 

 

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

 

@Desertir, это очень грубо и я бы даже сказал несуразно.

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

@Карлан, переопределение метода вообще моветон. Ктото из команды написал метод paint а ты его переопределяешь, и пишешь туда сложение, а тот первый пытается чтото вывести на экран. Получается вообще не пойми что и огрести можно не слабо. Я резко против этого, поэтому и написал, что есть if ... then.

я бы даже сказал несуразно

Про твой способ могу сказать тоже самое.

ТЧ 1.0004. SAP и Trans mod

github

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

@Desertir, :D с такими выводами можно функцию переписать с таким же успехом. В переопределении нельзя использовать if-then? Это не мой способ, но его для этого и создавали. Можно написать условия и для сложения и для вывода на экран, причем тут командная работа я вообще не понял.

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

 

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

 

Определение метода и его вызов, это просто сахар в Луа.

 

    Если функция определена как метод, то self при ее вызове  будет автоматически скрываться. Простая функция в этом случае будет вести себя несколько иначе - если вызвать ее как метод, параметры будут смещены вставкой self первым аргументом. Речь шла о замещении метода функцией, не расчитанной на получение self. И в первом посте было упомянуто - как вызвать метод (без двоеточия видимо?) и получить внутри него self не делая в заголовке каждой вариации метода лишних телодвижений с получением объекта, в котором метод был переопределен.

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

 

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

 

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

 

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

















class "element_class"
function element_class:__init(name)
    ...
    function element_class.inject_std(...) -- функция через точку
        -- выполняем стандартные действия, self - ссылка на этот объект
    end
    function element_class.inject_greatdata_debug(...)
        log("DL_CALL, ELEMENT_CLASS:120 call greatdata_debug")
        -- выполняем нужные действия
        self:set_param("inject_std") -- меняем поведение интерфейса inject

    end
    if <стандартное состояние> then
        self.inject = self.inject_std
    elseif <куча проверок> then
        self:set_param("inject_greatdata_debug")
    end
end
function element_class:inject(...)
    -- выполняем действия для неинициализованного состояния объекта
end
-- функция для смены состояния объекта и текущего обработчика, ее по идее бы
-- вообще реализовать в "приватной" форме, но пока не нашел элементарного варианта

function element_class:set_param(param) -- передаем имя нового обработчика, к примеру
    -- а здесь переключаем состояние объекта и меняем значение element_class.inject

    self.inject = self[param]
end

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

    В целом такой подход к написанию кода имеет много тонкостей и не подходит в реализации типичных алгоритмов. Но с его помощью вполне возможно реализовать нечто вроде "приватных" переменных/функций, которые никак не получить извне объекта.

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

чтобы он всегда соответствовал задаче, решаемой этим конкретным объектом на данный момент.

 

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

   В ходе своих наработок в Lua для сталкера сделал отметил несколько любопытных моментов:

 

1. По поводу функций table.insert, table.remove и оператора #:

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



for i = 1, #tb do tb[i] = nil end

   При этом функции insert и remove продолжают работать со старым значением индекса, а вот оператор получения длины выдает, что таблица пуста. Собственно смысл: если сейчас попытаться вставить в таблицу значение table.insert(tb, value), то оно встанет как раз по месту мифического конца индексированной части. И мы получим массив вида:

{nil, nil, nil, nil, nil, nil, nil, nil, nil, value}. В случае того же SciTe позиция последнего элемента обновляется при заниливании, а в сталкере этого не происходит.

   Собственно вопрос, можно ли как-то повлиять на значение этого внутреннего счетчика или следать метаметод, обновляющий его?

 

   Получается, что в сталкере задание значения по индексу n+1 счетчик попросту не обновляет, а функции вставки/удаления полей игнорируют текущую структуру таблицы.

 

   Upd: Забыл указать, версия ТЧ 1.0006

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

@Xdlic

После выполнения 

for i = 1, #tb do tb[i] = nil end

у меня и в SciTE, и в игре функции insert и remove работают одинаково правильно, т.е. ставит 'value' в поле с индексом 1.

Покажи весь код, на котором ты тестировал и получал таблицу {nil, nil, nil, nil, nil, nil, nil, nil, nil, value}

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

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

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

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

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

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

Войти

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

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

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