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

Язык Lua. Общие вопросы программирования


Malandrinus

Рекомендуемые сообщения

abramcumner,

Это тоже нюансы.

Как упомянул malandrinus

this - ссылка на текущий модуль (таблица)

Парсим _G и сравниваем с this.

Поделиться этим сообщением


Ссылка на сообщение

malandrinus,

То, что в Сталкере есть готовая функция - это конечно хорошо.

Но не для меня, раз уж мы в теме о lua. Ну нет, скажем, готовой.

Чем не подходит

Парсим _G и сравниваем с this

А говоря языком lua :

Там где нужно парсим _G и сравниваем с _M.

 

Поделиться этим сообщением


Ссылка на сообщение

Это известно.

Если нужно, можно поговорить и о чистом luа.

Но всё же делаем с оглядкой на сталкер. Dеbug исключается.

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

Поделиться этим сообщением


Ссылка на сообщение

Аrtоs,

да никто тут копья не ломает. Дискутируем, разбираемся, ищем, находим. Хорошо что есть с кем. А то молчат все, как будто всё знают.

Поделиться этим сообщением


Ссылка на сообщение

malandrinus,

Я же не говорю что перебор это панацея. Сам бы наверное поступил незатейливо.

Был задан несколько пространный вопрос. Зная что нужно, на зная точно для чего, достаточно тяжело сказать как.

Ведь я не зря вначале сказал, - "Нужно знать откуда и при каких условиях запрашивается это имя".

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

Варианты. Только и всего.

 

 

Поделиться этим сообщением


Ссылка на сообщение

Занимаясь кое-какими своими делами, случайно вспомнил (точнее сказать наткнулся) на довольно старую статью.

В своё время воспринял её как аксиому и...пользовался.

(Далее для начинающих)

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

Вопросы производительности

 

При разработке программ на Lua нужно иметь в виду, что компилятор этого языка не обладает широкими способностями по оптимизации. С другой стороны, интерпретатор Lua реализован очень эффективно и чаще всего обеспечивает весьма приличную скорость работы. Если же тебе нужна сверхвысокая производительность, ты должен сам следить за тем, чтобы в коде не было множества лишних конструкций. Добиться этого будет легче, если будешь придерживаться нескольких простых правил при написании кода на Lua (подробнее о подходах к оптимизации кода на Lua — в материалах, опубликованных в Lua Us-ers Wiki).

 

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

 

По возможности в коде, критичном по производительности, не создавай множество объектов Lua, управляемых сборщиком мусора. Такие объекты создаются при склеивании строк, вызове конструкторов таблиц (например при вызовах функций с переменным числом аргументов), при объявлении любых функций, при выполнении команд dofile/dostring.

 

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

 

= 2 * 34567 + b

на

a = 69134 + b.

 

Перевод скриптового кода в код на C++ — один из действенных методов оптимизации. Если тебе вдруг показалось, что какой-то участок кода на Lua нужно подвергнуть интенсивной оптимизации, может быть, ты обнаружил первый сигнал к тому, что этот кусок кода нужно переписать на C++.

 

Заботясь о производительности кода, остерегайся преждевременной оптимизации. По возможности соблюдай рекомендации по написанию оптимального по производительности кода — писать гарантированно медленный код не стоит. Однако, как известно, затраты, скажем, на оптимизацию куска кода, который выполняется не более 1% времени, не уменьшат временные затраты на выполнение программы больше чем на 1%.

 

Сначала реализуй код самым удобным (по твоим ощущениям) способом. Если понадобится, собирай статистику с помощью таких инструментов, как Lua Profiler (www.keplerproject.org/luaprofiler). И оптимизируй только то, что действительно должно быть оптимизировано.

Обработка ошибок

 

Важно помнить, что при небрежном отношении к обработке ошибок в Lua, после возврата ошибки из lua_pcall(), скриптовая система может оказаться в невалидном состоянии.

 

Безопаснее всего (по крайней мере, в стадии активной разработки) прекращать выполнение программы при возникновении ошибок в скрипте. Естественно, это не относится к консольному интерпретатору, в котором для комфортной работы нужно обеспечивать толерантность к возможным ошибкам и опечаткам. Тем не менее желательно принимать меры к тому, чтобы ошибки в коде, выполняемом с консоли, не приводили к неявной «порче» игрового мира.

ПРИ ВОЗНИКНОВЕНИИ ОШИБКИ В ФУНКЦИЯХ НА LUA ВМЕСТО ВЫЗОВА БИБЛИОТЕЧНОЙ ФУНКЦИИ ERROR() ВОЗВРАЩАЙ NIL И ТЕКСТ СООБЩЕНИЯ ОБ ОШИБКЕ.

 

ФУНКЦИИ, КОТОРЫЕ МОГУТ ВЫЗВАТЬ ERROR(), ВЫЗЫВАЙ (ГДЕ ВОЗМОЖНО) ЧЕРЕЗ БИБЛИОТЕЧНУЮ ФУНКЦИЮ PCALL(), ДЛЯ ЧЕГО ЛУЧШЕ ИСПОЛЬЗОВАТЬ ИДИОМУ PROTECT, ОПИСАННУЮ В СТАТЬЕ ДИЕГО НЕХАБА (DIEGO NEHAB) FINALIZED EXCEPTIONS (www.lua-users.org/wiki/FinalizedExceptions). ТАКЖЕ ПОЛЕЗНО ПОЛЬЗОВАТЬСЯ ИДИОМОЙ NEWTRY (СМ. ТУ ЖЕ СТАТЬЮ).

 

ПРОВЕРЯЙ ПЕРЕДАННЫЕ ФУНКЦИИ ПАРАМЕТРЫ НА NIL. ЕСЛИ, НАПРИМЕР, ФУНКЦИЯ, ОБЪЯВЛЕННАЯ С ТРЕМЯ ПАРАМЕТРАМИ, ВЫЗЫВАЕТСЯ С ДВУМЯ, ТО ТРЕТЬЕМУ ПАРАМЕТРУ БУДЕТ ПРИСВОЕН NIL.

 

ЕСЛИ ТВОЯ ФУНКЦИЯ РАБОТАЕТ С ГЛОБАЛЬНЫМИ ДАННЫМИ, ОСОБЕННО С ОПИСАНИЯМИ УРОВНЕЙ, ТЕКСТАМИ ДИАЛОГОВ И Т.П., ПО ВОЗМОЖНОСТИ ПРОВЕРЯЙ ЭТИ ДАННЫЕ НА NIL.

 

ОЧЕНЬ ЧАСТО ОШИБКИ ВОЗНИКАЮТ ИЗ-ЗА ТОГО, ЧТО ПРОГРАММИСТ ЗАБЫЛ НАПИСАТЬ КЛЮЧЕВОЕ СЛОВО LOCAL ПЕРЕД ПЕРВЫМ ПРИСВАИВАНИЕМ ЛОКАЛЬНОЙ ПЕРЕМЕННОЙ И ЗАТЕР ТАКИМ ОБРАЗОМ ГЛОБАЛЬНУЮ. СЛЕДИ ЗА ОБЛАСТЬЮ ВИДИМОСТИ ТВОИХ ПЕРЕМЕННЫХ. В НАЗВАНИЯХ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ ИСПОЛЬЗУЙ УНИКАЛЬНЫЙ ПРЕФИКС, НАПРИМЕР G_. ПРИМЕНЯЙ ИНСТРУМЕНТЫ ТИПА LUA LINT ДЛЯ ПРОВЕРКИ ИСПОЛЬЗОВАНИЯ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ. В ДИСТРИБУТИВ LUA 5.1 ВХОДИТ ПРИМЕР НА LUA, КОТОРЫЙ НАСТРАИВАЕТ МЕТАТАБЛИЦУ ТАБЛИЦЫ, СОДЕРЖАЩЕЙ ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ (”_G”), ТАКИМ ОБРАЗОМ, ЧТО ПРИ ДОБАВЛЕНИИ ИЛИ ЧТЕНИИ НЕЗАРЕГИСТРИРОВАННЫХ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ ВО ВРЕМЯ ВЫПОЛНЕНИЯ СКРИПТА ВЫДАЕТСЯ СООБЩЕНИЕ ОБ ОШИБКЕ.

 

СТАРАЙСЯ МИНИМИЗИРОВАТЬ ИСПОЛЬЗОВАНИЕ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ. СОБЛЮДАЙ МОДУЛЬНОСТЬ КОДА. СЧИТАЕТСЯ ХОРОШИМ ТОНОМ, ЕСЛИ ОДИН МОДУЛЬ СОЗДАЕТ ТОЛЬКО ОДНУ ГЛОБАЛЬНУЮ ПЕРЕМЕННУЮ. ПОДРОБНЕЕ О ПОДХОДАХ К ОРГАНИЗАЦИИ МОДУЛЕЙ В LUA МОЖНО ПРОЧИТАТЬ В LUA TECHNICAL NOTE 7: MODULES & PACKAGES РОБЕРТО ИЕРУСАЛИМСКОГО (www.lua.org/notes/ltn007.html).

 

ИЗБЕГАЙ СОЗДАВАТЬ НА СКРИПТОВОМ ЯЗЫКЕ СЛИШКОМ СЛОЖНЫЕ СИСТЕМЫ. ДИНАМИЧЕСКАЯ ТИПИЗАЦИЯ И ПРОЧИЕ ОСОБЕННОСТИ, ПРИ ВСЕМ СВОЕМ УДОБСТВЕ, ПЛОХО ВЛИЯЮТ НА СТАБИЛЬНОСТЬ, НАДЕЖНОСТЬ И ПРОИЗВОДИТЕЛЬНОСТЬ КОДА. СКРИПТОВЫЕ ЯЗЫКИ ВООБЩЕ И LUA В ЧАСТНОСТИ ХОРОШИ ДЛЯ «СКЛЕЙКИ» И НАСТРОЙКИ ФУНКЦИОНАЛЬНОСТИ ИГРОВОГО ДВИЖКА. ЕСЛИ ТЫ ВИДИШЬ, ЧТО КАКОЙ-ТО МОДУЛЬ НА LUA СТАНОВИТСЯ СЛИШКОМ СЛОЖНЫМ, ПОСТАРАЙСЯ ПЕРЕНЕСТИ ЕГО ФУНКЦИОНАЛЬНОСТЬ НА C++, ОСТАВИВ LUA ТОЛЬКО ВЫСОКОУРОВНЕВОЕ УПРАВЛЕНИЕ ЭТИМ МОДУЛЕМ.

 

Управление памятью

 

Язык Lua автоматически управляет памятью при помощи сборщика мусора (garbage collector): интерпретатор периодически вызывает сборщик мусора, удаляющий объекты, для которых была выделена память (таблицы, userdata, функции, потоки и строки) и которые стали недоступными из Lua («мертвые» объекты).

 

Интерпретатор Lua задает предел объема памяти (threshold), занимаемого данными. Как только занятый объем достигнет этого предела, запускается алгоритм, освобождающий память, занятую накопившимися «мертвыми» объектами. Затем устанавливается новый предел, равный двухкратному объему памяти, занимаемой после очистки.

 

Чтобы запустить сборку мусора немедленно, нужно программно установить предел занимаемой памяти в 0 (вызвав lua_setgcthreshold() из C или collectgar-bage() из Lua). Чтобы остановить сборку мусора, устанавливаем этот предел в достаточно большое значение.

 

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

 

При написании программ на Lua обязательно учитывай то, каким образом интерпретатор Lua управляет распределением памяти. Сборка мусора — относительно затратная по производительности операция (используется алгоритм non-incremental mark and sweep). Она инициируется автоматически, когда объем выделенной памяти превышает двукратный объем памяти, оставшейся выделенной после предыдущей сборки мусора (объем выделенной памяти, при превышении которого произойдет следующая сборка мусора, также можно задавать программно). Значит, фактически, в достаточно большой динамической системе сборка мусора может быть запущена в произвольный момент времени, вызвав просадку по производительности. Чем больше памяти выделено под объекты Lua, тем дольше происходит сбор мусора.

 

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

 

Код создает новую строку (и присваивает ее переменной some_string), а строка, хранившаяся в some_string до склейки, остается в памяти до следующей сборки мусора:

 

some_string = some_string .. "a"

 

Такая проблема и способы ее решения описаны в статье Роберто Иеруса-лимского Lua Technical Note 9: Creating Strings Piece by Piece (www.lua.org/notes/ltn009.html). В Lua 5.1 реализована инкрементальная сборка мусора, позволяющая распределить нагрузку по производительности от процесса сборки мусора во времени

 

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

Поделиться этим сообщением


Ссылка на сообщение

аlреt

Неужели ты считаешь, что такая информация может быть проигнорирована? :)

Можно подробности?

Это только для контекста Сталкера, или для luа в целом?

Поделиться этим сообщением


Ссылка на сообщение

*Shoker*

ИМХО, гораздо интереснее смотрится решение твоего вопроса с использованием pipe.

--============================================================
-- Открыть файл
io.popen("D:\\name_file.txt")
--============================================================
-- Чтение из файла построчно
for line in io.popen("type D:\\name_file.txt"):lines() do
    -- чего-то там делаем со строкой "line"
end
--============================================================
-- Запуск CMD и вывод в него содержимого файла
io.popen('start cmd /k type D:\\name_file.txt')
-- или сообщения
io.popen('start cmd /k echo message')
--============================================================
-- Получение результата работы команд (напр. 'echo', 'dir')
local f = io.popen('echo message',"r")
local s = f:read("*a")
print(s)

-- Получение из директории С имён файлов с расширением *.txt
for line in io.popen("dir c:\\"):lines() do
    if line:match("%.txt$") then
        -- чего-то там делаем со строкой "line"
    end
end

 

 

Поделиться этим сообщением


Ссылка на сообщение

*Shoker*

Пиши так :

os.execute([["D:\\name file.txt"]])
-- или
io.popen([["D:\\name file.txt"]])

Двойные кавычки в CMD защитят от пробелов в именах директорий\файлов.

 

Различия между "string" и 'string' в контексте lua нет никакого. Кому как удобнее, тот так и пишет.

А вот с использованием os.execute могут быть проблемы из-за синтаксиса CMD.

Поизучай его.

Я конечно совершил ошибку, что сразу не написал корректный код. Извиняюсь. Упустил.

 

Русские буквы не должны мешать.

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

Поделиться этим сообщением


Ссылка на сообщение

А я разве сказал писать"start" в начале?

Посмотри внимательнее мой код.

Хотя я имею в виду просто открытие файла, а у тебя может быть что-то другое.???

Напиши

os.execute('"'..log_path..'"')

Вот блин. Так непонятно. Так :

os.execute([["]]..log_path..[["]])

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

Поделиться этим сообщением


Ссылка на сообщение
Ужос о_О

Но на удивление сработало

Почему ужас?

И почему - на удивление?

Все и должно было сработать.

 

Поделиться этим сообщением


Ссылка на сообщение

Какая-то ерунда получается!

Ах, да. Забыл рассказать начало истории :)

Решил (по)использовать макет luabind для академического lua версии xStream (этот код был любезно предоставлен нам несколько выше).

Всё очень замечательно, но...

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

Итак.

При объявлении класса (class "Х") в таблице _G создается поле-таблица(класс).

Пока всё верно.

Далее (по чтению кода) в этот класс добавляются поля. Но эти поля являются только методами (т.е. функциями). А где же свойства???

Так вот эмоции у меня вызвал метод "__init". В нём вроде бы и есть свойства, но...

Судя по коду метод "__init" вызывается только в одном случае. А именно при создании объекта класса. Т.е. все данные этого метода (а именно искомые свойства) записываются в таблицу создаваемого объекта. А значит, что при создании нескольких объектов класса, в каждый из них будут записаны эти свойства.

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

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

Тогда остается одно. Обернуть назначение свойств класса в метод, и вызвать этот метод в необходимое время.

Что собственно пока и решает вопрос, но хочется знать, - "Как всё-таки сделать правильнее всего?"

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

Поделиться этим сообщением


Ссылка на сообщение

Artos

В общем так и поступил. Переписал luаbind по-своему.

Теперь работает как нужно.

То что не все свойства присваиваются сразу это понятно. Но мне нужно было обьявить в классе базовые свойства сразу при создании.

Короче, эмоции уже успокоились:)

Поделиться этим сообщением


Ссылка на сообщение
Извиняюсь за дурацкий вопрос, но где можно найти расшифровку клавиш, которая приводится допустим в "Классе кнопок" для Луа.
Вопрос конечно из категории - "Для ленивых"

Запусти тот же ... ну хоть Сократ ...

***ADD = 78;          -- +
***APOSTROPHE = 40;   -- `
***AT = 145;          -- @
***AX = 150;          -- #
***CAPITAL = 58;      -- $
***CIRCUMFLEX = 144;  -- ^
***COLON = 146;       -- :
***COMMA = 51;        -- ,
***DIVIDE = 181       -- /
***EQUALS = 13;       -- =

Примерно так, что ли?.....

Поделиться этим сообщением


Ссылка на сообщение

Dennis_Chikin

С точки зрения lua код абсолютно нормальный. И результаты получаются правильные.

Т.е. количество полей - 9, индексы от 1-го до 9-и включ.,значения тоже.

Единственное что может его "портить", это _util["list_tbl"]( t, "test_tbl" ).

Тем более ты писал, - "(list_tbl - там много всякого...)"

Ну или пресловутая "аномальность" lua версии Сталкер.

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

Поделиться этим сообщением


Ссылка на сообщение

Да, пробовал в SciTE.

И индексация последовальная от 1=1, ... ,9=9. Как, собственно, и должно быть.

Ещё раз скажу, показанный тобою пример не портит массив, как последовательность поступательно-возрастающих, целочисленных индексов, начинающихся с единицы (и ещё раз повторю - в нормальном lua).

Artos

Я беру только данный пример.

До сиз пор не могу въехать, что же именно нужно было изначально. (С разбором желаемого до атомов).

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

Поделиться этим сообщением


Ссылка на сообщение

Во всех учебниках, по которым я изучал lua, пишут, что lua до последнего старается сохранять массив именно как массив,а не как хэш.

Что и показывают результаты SciTE.

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

Поделиться этим сообщением


Ссылка на сообщение

Тогда дайте хоть постебаться:)

Для тех кому ну очень уж хочется добавлять элементы в таблицу методом t[k]=v

mt={__newindex = function(t,k,v) table.insert(t,k,v) end}
local t={}
setmetatable(t,mt)

t[1]=1
t[2]=2
t[3]=3
t[4]=4
...

Поделиться этим сообщением


Ссылка на сообщение

Callisto

Если по-быстрому, то можно

Измени таблицу

table_2 = {
["String_1"] = true,
["String_4"] = true,
["String_7"] = true,
["String_10"] = true
}

И функцию

function Update()
    local txt = table_1[math.random( #table_1 )]
    if table_2[txt] then
        txt = txt.." "..table_3[math.random( #table_3 )]
    end
    
    return  txt
end

 

Поделиться этим сообщением


Ссылка на сообщение

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

Не буду говорить о скоростях перебора и поиска по индексу в хэше, но эстетически выглядит подобный вариант, ИМХО, некрасиво.

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

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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