Александр
Не, про package я в курсе... Я не знал, что его так варварски можно менять. И, кстати, как показала практика, можно хакать даже те имплементации, где смена Package вообще невозможна...
Не варварски, а системно - например можно тупо через переменные настраивать среду, это +- универсально, тогда как в случае ручной настройки вы будете жестко привязаны к библиотекам которые носите с собой, что черевато тем, что в системе будет куча версий одинаковых библиотек
Александр
Другими словами, через переменные можно получить унификацию среды, ручной настройкой эту гибкость вы теряете и тут собственно логика такова, что переменные правильно использовать в случае хорошо настроенной среды, руками - в случае если нужно таскать с собой кастомные библиотеки
Денис
В общем, если кому-то, как мне, понадобится собирать свою песочницу для приложения, делюсь пусковым VBS. Мне его в начале очень не хватало, поэтому кидаю сюда: ' Initialization parameters AppContainer = "YourApp.luac" ' Either the pre-compiled LuaC app container or app startup source file ContainerPlace = "app\" ' The Lua files where your application placed Sandbox = "sandbox\" ' The interpreter environment folder where it is placed by per every architecture LuaVersion = "53" ' The Lua version which it should search in every library or interpreter startup executable app. ' ==========' ' Main body script Set WshShell = CreateObject("WScript.Shell") if right(WshShell.environment("system").item("processor_architecture"), 2) = "64" then Architecture = "x64" else Architecture = "x86" end if Set procEnv = WshShell.environment("PROCESS") procEnv("LUA_CPATH") = WshShell.CurrentDirectory & "\" & sandbox & "\" & Architecture & "\?.dll;" & WshShell.CurrentDirectory & "\" & sandbox & "\" & Architecture & "\?" & LuaVersion & ".dll" procEnv("LUA_PATH") = WshShell.CurrentDirectory & "\" & sandbox & "\modules\?.lua;" & WshShell.CurrentDirectory & "\" & sandbox & "\modules\?\init.lua" WshShell.Run(WshShell.CurrentDirectory & "\" & Sandbox & Architecture & "\wlua" & LuaVersion & ".exe" & Chr(32) & Chr(34) & ContainerPlace & AppContainer & Chr(34)) Set WshShell = Nothing Может не совсем логичная структура, но тут уже каждый поправит под себя. Главное - есть толчок. И да, все это запускается без промежуточных консолей. Структура же папок следующая: app : папка с вашим приложением на Lua sandbox : Песочница интерпретатора. Должна содержать следующую структуру: x64 : все необходимое для 64-разрядной ОС, включая интерпретатор и C-библиотеки x86 : все то же самое, только для 32-разрядных ОС modules : shared-модули, написанные на lua, которые будут подключаться отдельно в вашем приложении (можно конечно собирать в пре-компилированный luac) Название папок разумеется можно менять легким движением руки (вверху в переменных). Может всем покажется такое не нужно, но я вот такой лаунчер писал вполне себе долго, не зная VBS и объекта WScript.Shell, и, если бы нашел что-то вроде этого, моя жизнь бы сильно упростилась. К тому же, данный лаунчер учитывает ошибки прошлых дней и задает свои параметры package, которые дальше, чем для этого процесса, никуда не пойдут.
Денис
В общем, если кому-то, как мне, понадобится собирать свою песочницу для приложения, делюсь пусковым VBS. Мне его в начале очень не хватало, поэтому кидаю сюда: ' Initialization parameters AppContainer = "YourApp.luac" ' Either the pre-compiled LuaC app container or app startup source file ContainerPlace = "app\" ' The Lua files where your application placed Sandbox = "sandbox\" ' The interpreter environment folder where it is placed by per every architecture LuaVersion = "53" ' The Lua version which it should search in every library or interpreter startup executable app. ' ==========' ' Main body script Set WshShell = CreateObject("WScript.Shell") if right(WshShell.environment("system").item("processor_architecture"), 2) = "64" then Architecture = "x64" else Architecture = "x86" end if Set procEnv = WshShell.environment("PROCESS") procEnv("LUA_CPATH") = WshShell.CurrentDirectory & "\" & sandbox & "\" & Architecture & "\?.dll;" & WshShell.CurrentDirectory & "\" & sandbox & "\" & Architecture & "\?" & LuaVersion & ".dll" procEnv("LUA_PATH") = WshShell.CurrentDirectory & "\" & sandbox & "\modules\?.lua;" & WshShell.CurrentDirectory & "\" & sandbox & "\modules\?\init.lua" WshShell.Run(WshShell.CurrentDirectory & "\" & Sandbox & Architecture & "\wlua" & LuaVersion & ".exe" & Chr(32) & Chr(34) & ContainerPlace & AppContainer & Chr(34)) Set WshShell = Nothing Может не совсем логичная структура, но тут уже каждый поправит под себя. Главное - есть толчок. И да, все это запускается без промежуточных консолей. Структура же папок следующая: app : папка с вашим приложением на Lua sandbox : Песочница интерпретатора. Должна содержать следующую структуру: x64 : все необходимое для 64-разрядной ОС, включая интерпретатор и C-библиотеки x86 : все то же самое, только для 32-разрядных ОС modules : shared-модули, написанные на lua, которые будут подключаться отдельно в вашем приложении (можно конечно собирать в пре-компилированный luac) Название папок разумеется можно менять легким движением руки (вверху в переменных). Может всем покажется такое не нужно, но я вот такой лаунчер писал вполне себе долго, не зная VBS и объекта WScript.Shell, и, если бы нашел что-то вроде этого, моя жизнь бы сильно упростилась. К тому же, данный лаунчер учитывает ошибки прошлых дней и задает свои параметры package, которые дальше, чем для этого процесса, никуда не пойдут.
И да, замечания и улучшения приветствуются. 🙂
Денис
Чото соседний чат подвергся нападению ботов...
Snusmumriken
Это бывает.
Snusmumriken
Луа входит в кейворд "бизнеса".
Денис
Та ну его, тем более, что там ничего полезного
Snusmumriken
Ну, ссылка на этот чат некоторое количество раз мелькала на хабре, и это с одной стороны сделало его популярнее, с другой — сюда тоже время от времени налетают боты. Иногда пачками по 50 штук.
Денис
А порой даже не какой-то, а вполне себе веселый)))
Hell
Приветствую господа товарисчи, есть возможно кто работал над игрой World of Warcraft именно на lua. Есть заказ, нужно сфомировать запрос с отправкой на сервер, или переписать код близзов. Готов оплатить время и работу. Если есть такие - отпишите
Hell
чуть позже отпишу
Snusmumriken
Потому что я лишний раз напоминаю: близзы ничего не дадут отправлять на сервер. Ты не можешь просто взять и отправить что-то из игры на сторонний сервер. Для отправки используют сторонние приложения, вроде raider_io/wowhead tracker, то есть аддон сбрасывает данные в файлик на диске, его подхватывает стороннее приложение и отправляет, и наоборот. Скорость приём-передачи в среднем около 1-2 запросов в сутки. "Переписывать" же код близзов == нарушать соглашение и заниматься хакерским хакингом. Поэтому ещё раз спрашиваю: что конкретно нужно?
jon
Всем привет, где можно почитать то, как работают всякие функции луа? На lua.org как-то куцо. Вот например string.sub. Потыкавшись в дебаг, я вижу, что эта функция возвращает строку не больше, чем она есть. Т.е какой нибудь мусор не скушает, если укажу конец строки за пределами строки. И вот не могу понять, это и нормальное поведение или как повезёт
Snusmumriken
Например, достаточно исчерпывающая информация по sub.
Snusmumriken
Книжка Роберто как раскрытие этих функций и примерчики, но тут — основная инфа. В своё время просто перебирал вообще всё и тут же ковырял, чтобы усвоить.
jon
Правильно ли я понимаю, что если я хочу обходить таблицу типа table = { {1,2,3}, {2,3,4} } В +- красивом виде типа for a, b, c in... То надо итератор свой писать?
Snusmumriken
Правильно ли я понимаю, что если я хочу обходить таблицу типа table = { {1,2,3}, {2,3,4} } В +- красивом виде типа for a, b, c in... То надо итератор свой писать?
Да. Или превратить в список. Или воспользоваться двумя итераторами: for i, row in ipairs(table) for j, column in ipairs(row) do ... end end Это проще и надёжнее.
jon
Да. Или превратить в список. Или воспользоваться двумя итераторами: for i, row in ipairs(table) for j, column in ipairs(row) do ... end end Это проще и надёжнее.
Я похоже в итоге сделал, просто потом в цикле определил переменные через элементы массива
Snusmumriken
Меня в своё время больше интересовали итераторы по определённым диапазонам таблиц
jon
Мне пока семантика итераторов луа как то не даётся, не могу понять, почему они работают так как работают
Snusmumriken
Работают очень просто. for i, v in ipairs(tbl) do ... end == local iter, tbl, i = ipairs(tbl) local i, v = iter(tbl, i) while i do ... i, v = iter(tbl, i) end То есть буквально так, конструкция in это сахар для этого. И в пределах этой конструкции ты можешь делать что хочешь — мутить лямбды-итераторы-генераторы я ля питоновый range, делать stateless-итераторы которые не создают новых функций но используют существующие и т.д.
Денис
Мне пока семантика итераторов луа как то не даётся, не могу понять, почему они работают так как работают
Я тоже долгое время их не понимал, просто использовал. Потом задался целью понять и пописать свои. В общем, это выглядит примерно так: for значения, из, итерирующей_функции in итерирующая_функция, объект_итерации do end А фабрика итераторов как раз и возвращает итерирующую функцию и объект, то есть for значения, из, итерирующей_функции in фабрика_итераторов(объект_итерации) do end
jon
Ну как их использовать понимаю) но вот как итерирующая функция внутри выглядит, и почему она такая, я до конца понять не могу, в тех же плюсах все понятнее в этом плане
Snusmumriken
К сожалению тут есть некоторое количество ограничений например на количество аргументов из функции-итератора, поэтому чтобы сделать что-то сложное и "stateles", приходится пилить таблицу-хранилище для множества аргументов, и пользоваться примерно так: for itble, value in slice(tbl, i, j) do print(itble.i) --> 1, 2, 3 и т.д. print(itble.max) --> j end Потому что генерировать новые функции с замыканиями для таких задач выглядит дороговато. Делать таблицу-хранилище на самом деле тоже, и от этого несколько неприятно.
Денис
То есть, та же самая функция фабрики итераторов pairs() возвращает два параллельных значения: какбэ указатель на функцию next (стандартно есть в Lua, возвращает следующий элемент таблицы) и саму таблицу. В свою очередьь next() возвращает параллельные значения - ключ, значение.
Snusmumriken
Да, вместо pairs можно использовать next, но в чуть другой форме: for k, v in next, tbl do ... end То есть next это функция-итератор а tbl — аргумент который in будет принимать.
Snusmumriken
А кстати, сколько ограничение?
На вход лямбде итератора приходит всего 2 аргумента включая таблицу, остаётся один свободный, и обычно это индекс из которого делается следующий индекс. Если через функцию-генератор, то можно хоть for i, j, k, m, n, v in myiter(tbl) do end Но myiter должна возвращать новую функцию с замыканием, или функция с замыканием должна генерировать выводную часть всего из двух аргументов tbl и i. И если i сделать таблицей для дополнительной инфы из которой можно генерить остальные аргументы, то можно и stateless.
Snusmumriken
А, ну это-то да. А если наша самописная фабрика итераторов возвращает самописный итератор, который в свою очередь возвращает нужное количество значений?
Вот я говорю, лямбда может возвращать сколько угодно, но на вход получает только два. А из двух много обычно не сделаешь.
Snusmumriken
Например, вот прекрасный итератор по utf8-символам: string.charpattern = "[\1-\x7F\xC2-\xF4][\x80-\xBF]*" local cp = string.charpattern function utf8_next(line, index) local a, b = line:find(cp, index) if not a then return end return b + 1, line:sub(a, b) end function string:utf8chars() return utf8_next, self, 1 end У него в качестве индекса используется конец текущего utf8-символа + 1. Поэтому он малоюзабельный в итераторе и фактически указывает на длину до текущего куска строки в байтах. Использовать индекс utf8-символа крайне невыгодно. Добавить его отдельно — проблематично из-за stateless.
Денис
По сути, ты же под капот не лезешь, пусть там хоть userdata приходит, главное, чтобы итератор смог ее обработать)))
Snusmumriken
Ну это то что будет первым возвращаемым аргументом, и желательно чтобы оно было юзабельным для пользователя.
Денис
Ну это то что будет первым возвращаемым аргументом, и желательно чтобы оно было юзабельным для пользователя.
Не, смотри: наша фабрика итераторов принимает на вход некое значение. Под капотом Формирует лямбду, а вторым параллельным значением возвращает новый объект, который юзеру видеть не надо, это чисто внутренний служебный. Наша же лямбда принимает этот служебный объект, в который мы можем вообще принять всего один аргумент (второй просто проигнорировать), а в этом объекте например сохранить переданный нам в фабрику объект и нужные значения. Ну а далее итератор возвращает значения, с которыми нужно взаимодействовать.
Snusmumriken
Смотри, лямбда принимает некоторый индекс и должна возвращать его же первым аргументом, потому что его ей передадут при следующем вызове. Или лямбда должна быть именно лямбдой и хранить аргументы в замыкании, тогда ей вообще пофигу что в неё будут пропихивать, она сама будет генерить аргументы и возвращать, ибо замыкание. Служебный объект может сидеть там же и не высовываться. Но если добавляем зависимость от аргумента при следующих вызовах, то его надо возвращать первым.
Snusmumriken
Да, который она возвращала
Snusmumriken
Например для next это имя предыдущего ключа, чтобы можно было получить следующий.
Денис
Да, который она возвращала
Ну так и пусть отправляет, мы же его проигнорить можем)))
Snusmumriken
Ну это не очень красиво для юзера )
Snusmumriken
Так что проще кажется делать натуральную независящую лямбду с замыканием. Но только в тех итераторах которые редко вызываются и на которые пофигу.
Денис
Ну это не очень красиво для юзера )
Так а юзер этого и не видит. Мы же вообще можем одно значение из итератора возвращать
Snusmumriken
Блин, я уже хотел нарисовать схемку, пока не вспомнил что с этим проблемсы )
Денис
А там что происходит под капотом - это уже юзеру видеть не надо. Ну а если он хочет посмотреть - ну что ж, флаг ему в руки, пусть смотрит, тут и так порой смотришь на код и у тебя мозг подрывается)))
Snusmumriken
Карочи, всегда где есть возможность, я предпочитаю делать stateless-итератор, который по-человечески принимает аргументы и делает всю работу из некоторого индекса.
Snusmumriken
local function ripairs_next(tbl, index) index = index - 1 local v = tbl[index] if not v then return end return index, v end function M.ripairs(tbl, index) assert(type(tbl) == "table", "Arg #1 error: table expected, got " .. type(src)) index = index or #tbl index = index > 0 and index - 1 or index return ripairs_next, tbl, index % #tbl + 2 end
Денис
Тиха тиха тиха!
Денис
Не сбивай)))
Snusmumriken
Лады, просто тут уже всё работает и отлажено и stateless и можно выбрать начальную точку итерации )
Денис
function ipairsstep(t, startFrom, step) local startIteration = true local lambda = function (obj, index) local tbl = obj.origTable local objstep = obj.step index = index or obj.startFrom if startIteration then startIteration = false else index = index+tonumber(objstep) end if tbl[index] then return index, tbl[index] end end return lambda, {origTable=t,step=step, startFrom=startFrom} end -- Test cts = {"One", "Two", "Three", "Four", "Five"} for k, v in ipairsstep(cts, 1, 1) do print("key: ", k, "value: ", v) end for k, v in ipairsstep(cts, #cts, -1) do print("key: ", k, "value: ", v) end
Денис
Разумеется, шаг можно было вынести и в локальные переменные фабрики, но такое решение чисто для наглядности.
Денис
Да и стартовый шаг тоже можно было вынести)))
Денис
У меня вот тут другая проблема... Я никак не соображу как в C-библиотеке сопоставить юзердату метатаблице типа...
Денис
https://www.lua.org/manual/5.1/manual.html#luaL_checkudata
Это-то да, только таким образом я могу читать сопоставленную юзердату, а вот как ее объявить и прицепить к ней методы...
Igor
А, кстати, для 5.1 нет этого метода, надо использовать lua_setmetatable
Igor
Нашел другое
Этот метод каждый раз создаёт метатаблицу
Igor
Что не есть хорошо, если объектов будет туева хуча
Igor
А, не, один раз. Всё нормально, я не увидел там luaL_newmetatable.
Igor
Ну а функции в метатаблицу (если их много) можно задать через luaL_Reg структуры и функцию регистрации, чтобы не писать кучу пушей самому
Денис
У меня тут вся проблема в том, что я пишу на языке, который крутится в Widechar, а Lua кушает исключительно MultiByte. Так что мне тут еще приходится мудрить с закрытиями памяти
Денис
А еще я писал создание самой метатаблицы в luaopen_, и теперь мне надо сообразить ккак уже созданную метатаблицу сопоставить, но докопаюсь
Igor
Какая версия Lua?
Igor
Отлично, тогда через luaL_getmetatable её в стек отправляй