дублер каскадера АЛЕКСАНДР ГЛАДЫШ Спецвыпуск: Хакер, номер #071, стр. 071-068-7 [2] = { type_ = “button”; name_ = ”settings”; }; [3] = { type_ = “button”; name_ = ”exit”; }; }; }; [2] = { type_ = “panel”; name_ = “settings”; … }; }; }; [«самозагружающиеся» данные.] Уже в описанном виде с данными можно эффективно работать. Однако это не всегда удобно – если сохраненные данные соответствовали каким-то «живым» объектам логики приложения, придется писать отдельный код по созданию и настройке этих объектов на основе получившихся таблиц. Было бы удобнее создавать объекты сразу же. Это вполне возможно. Один из путей – встроить код создания объектов логики в функции-модификаторы, возвращаемые дочерними ключами, и изменить логику обхода потомков. Если потомку нужна информация о предке, нужно делать обход потомков не сразу при вызове функции – ключевого слова, как сделано выше, - а начиная с корневого элемента. При этом нужно дополнительно передавать модификаторам данные о предке. Пример для случая, когда для создания нашего элемента пользовательского интерфейса нужна информация о родительском окне: panel= name_data(“panel”) function(name, data) return function(data, parent_wnd) local wnd = Window.Create(name, parent_wnd) process_child_keywords(data, wnd) end end) Подразумевается, что функция process_child_keywords была модифицирована, чтобы передавать вместе с данными предка еще один параметр – его окно. Нужно модифицировать и реализацию ключевых слов без параметров. При таком подходе они должны уметь изменять свойства самого окна – изменять данные уже поздно. Например, для hidden: hidden = function(data, wnd) wnd:SetVisible(false) end Если параметр, изменяемый ключевым словом без аргумента, должен быть доступен на этапе до того, как возможна обработка всех потомков (например, если в приведенном примере нужно передать модальность окна в Window.Create), необходимо реализовать тем или иным способом обход потомков в два прохода. Самый простой способ – вложить функцию-модификатор параметризованного ключевого слова в еще одну такую же и вызывать process_child_keywords дважды (такую реализацию можно абстрагировать подобно name_data): panel= name_data(“panel”) function(name, data) return function(data) return function(data, parent_wnd) process_child_keywords(data) – Data modifiers local wnd = Window.Create(name, parent_wnd, data.modal) process_child_keywords(data, wnd) -- Child windows end end end) [кодогенерация.] Видно, что с увеличением сложности структуры данных усложняется и реализация ключевых слов, и сопутствующего им кода. Растут накладные расходы по производительности загрузки данных. В последнем примере требуется, как минимум, пять вложенных вызовов функций, прежде чем дело доходит до непосредственного создания объекта. В действительности это не очень страшно – виртуальная машина Lua обладает очень высокой производительностью и, к тому же, поддерживает «хвостовые» вызовы функций (tail function calls) (в частности, хвостовую рекурсию tail recursion), что позволяет эффективно оптимизировать вызовы вида return functioncall и снимает ограничение на количество вложенных вызовов функций такого рода. |