Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Поиск Объектов, по имени, из списка отображения. (http://www.flasher.ru/forum/showthread.php?t=174616)

Vitalya 29.01.2012 19:44

Поиск Объектов, по имени, из списка отображения.
 
Ув. Flash/Flex программисты нужно решить задачу, если у кого есть готовое решение не откажусь.
Насколько я понимаю здесь нужно реализовать обход дерева, пока не найдем нужный DisplayObject...
Я реализовал обход дерева, просто развернув его в Vector, но такой подход медленнее чем РЕКУРСИВНЫЙ.
Да и РЕКУРСИВНЫЙ ПОДХОД ТОЖЕ НЕ КАТИТ((

Вот примерный прототип функции:
public static DisplayObject searchByNameDO( o: DisplayObjectContainer, oname: String ): DisplayObject{
}

silin 29.01.2012 19:59

надо бы уточнить ограничения, почему не катит рекурсия и т.п.
чем например такой (в лоб, без затей) не подходит ?
Код AS3:

public static function searchByNameDO(o:DisplayObjectContainer, oname:String):DisplayObject
{
        for (var i:int = 0; i < o.numChildren; i++)
        {
                var child:DisplayObject = o.getChildAt(i);
                if (child.name == oname)
                {
                        return child;
                }else if( child  is DisplayObjectContainer)
                {
                        return searchByNameDO(child as DisplayObjectContainer, oname);
                }
        }
        return null;
}


Vitalya 29.01.2012 20:02

Шеф сказал без Рекурсий(( Объяснил мне тем, что нужна скорость больше...
P.S. Согласись неопровержимый факт)

wvxvw 29.01.2012 20:18

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

ChuwY 29.01.2012 20:33

В порядке бреда:
Можно заносить всех детей в нужную вам структуру данных по Event.ADDED, ловящемся на контейнере (корне дерева), например.
Если цепляется лист -- хорошо.
Если цепляется поддерево -- его обходим рекурсивно, но это незначительно при малых поддеревьях.

Vitalya 29.01.2012 20:56

Спасибо, я понимаю что может рекурсия самый оптимальный вариант, но так-как я на испытательном сроке, то что-то доказывать шефу не стану((
Как я в самом начале пытался делать:

getChildByName(name:String):DisplayObject
Возвращает экземпляр дочернего экранного объекта с заданным именем.
1. Прогоняю корень через getChildByName, если результата нет.
2. Иду вниз по всем детям пока не найду нужный либо дерево не закончиться.
!!! Но в процессе реализации запутался очень с тим ДЕРЕВОМ, удалил весь код и написал развертку дерева в Vector и после пробегал по вектору getChildByName-ом. Мне шеф "забанил" работу с вектором, подскажите оптимальный алгоритм пробега по n-арному дереву.

Stitch512 29.01.2012 21:07

Код AS3:

public static function searchByNameDO(owner: DisplayObjectContainer, name: String): DisplayObject
{
        var stackContainers:Vector.<DisplayObjectContainer> = new Vector.<DisplayObjectContainer>();
        var stackIdx:Vector.<int> = new Vector.<int>();
 
        var currContainer:DisplayObjectContainer = owner;
        var idx:int = 0;
 
        while (currContainer != null)
        {
                var result:DisplayObject = currContainer.getChildByName(name);
                if (result != null)
                        return null;
 
                while (idx < currContainer.numChildren)
                {
                        var container:DisplayObjectContainer = currContainer.getChildAt(i)  as DisplayObjectContainer;
                        if (container != null)
                        {
                                stackContainers.push(currContainer);
                                stackIdx.push(idx);
 
                                currContainer = container;
                                idx = 0;
 
                                break;
                        }
 
                        idx++;
              }
 
              currContainer = stackContainers.pop();
              idx = stackIdx.pop();
        }
}

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

Добавлено через 7 минут
Цитата:

!!! Но в процессе реализации запутался очень с тим ДЕРЕВОМ, удалил весь код и написал развертку дерева в Vector и после пробегал по вектору getChildByName-ом. Мне шеф "забанил" работу с вектором, подскажите оптимальный алгоритм пробега по n-арному дереву.
Код AS3:

public static function searchByNameDO(container: DisplayObjectContainer, name: String): DisplayObject
{
        var result:DisplayObject = container.getChildByName(name);
        if (result != null)
                return null;
 
        for (var i:int = 0; i<container.numChildren; i++)
        {
                var childContainer:DisplayObjectContainer = container.getChildAt(i) as DisplayObjectContainer;
                if (childContainer != null)
                {
                        result = searchByNameDO(childContainer);
                        if (result != null)
                                return result;
                }
        }
 
        return null;
}


alatar 29.01.2012 21:27

Насколько часто у вас меняется display list?

Добавлено через 2 минуты
Ну и смысл задачи не ясен. Вам надо найти все DO с определенным именем или только первый.

Vitalya 29.01.2012 21:45

Насчет изменений дисплей листа меня не уведомили, но завтра на месте спрошу. Думаю смысл задачи вернуть хотя-бы первый попавшийся объект с заданным именем, без использования РЕКУРСИИ. Буду благодарен за любые варианты решений!)

Добавлено через 1 час 5 минут
Ув. Stitch512, у меня пару вопросов по исходнику, который не рекурсивный.

Код AS3:

public static function searchByNameDO(owner: DisplayObjectContainer, name: String): DisplayObject
{
        var stackContainers:Vector.<DisplayObjectContainer> = new Vector.<DisplayObjectContainer>();
        var stackIdx:Vector.<int> = new Vector.<int>();
 
        var currContainer:DisplayObjectContainer = owner;
        var idx:int = 0;
 
        while (currContainer != null)
        {
                var result:DisplayObject = currContainer.getChildByName(name);
                if (result != null)
                        return null;//ЗДЕСЬ return result; ???
 
                while (idx < currContainer.numChildren)
                {
                        var container:DisplayObjectContainer = currContainer.getChildAt(i)  as DisplayObjectContainer;
                        //ЗДЕСЬ вместо i - idx или просто наращивать i ниже?
                        if (container != null)
                        {
                                stackContainers.push(currContainer);
                                stackIdx.push(idx);
 
                                currContainer = container;
                                idx = 0;
 
                                break;
                        }
 
                        idx++;
              }
 
              currContainer = stackContainers.pop();//Что здесь происходит?
              idx = stackIdx.pop();//Что здесь происходит?
        }
        return null;
}

Заранее благодарен за ответ!)

alatar 29.01.2012 23:13

Вот вам без рекурсии.
Код AS3:

private const objectsMap:Object = {};
...
stage.addEventListener(Event.ADDED_TO_STAGE, stage_addedToStagehandler, true);
stage.addEventListener(Event.REMOVED_FROM_STAGE, stage_removedToStagehandler, true);
...
protected function stage_removedToStagehandler(event:Event):void
{
    objectsMap[event.target.name] = event.target;
 
}
protected function stage_addedToStagehandler(event:Event):void
{
    delete objectsMap[event.target.name];
}
 
private function searchByName(name:String):DisplayObject
{
    return objectsMap[name];
}

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

in4core 29.01.2012 23:16

Цитата:

Но, возвращать будет всегда последний добавленный в список отображения объект с именем name,
Код AS3:

private function searchByName(name:String):DisplayObject
{
    for(var i:String in objectMap) if ( i == name ) return objectMap[i];
    return null
}


silin 29.01.2012 23:29

in4core, такой цикл вернет ровно тоже самое, что и objectsMap[name] :)
имелось ввиду, что что если объектов с одинаковым именем будет не один, то хранится будет последний добавленный

in4core 29.01.2012 23:31

аа точно, затупил, сорри.

Stitch512 29.01.2012 23:49

Цитата:

return null;//ЗДЕСЬ return result; ???
Цитата:

//ЗДЕСЬ вместо i - idx или просто наращивать i ниже?
Да, все правильно, должно быть
Код AS3:

return result;

а вместо i - idx. Писал скорее как превдокод, поэтому работоспособность не проверял, сори)
Цитата:

currContainer = stackContainers.pop();//Что здесь происходит?
idx = stackIdx.pop();//Что здесь происходит?
Проходимся по списку отображения, когда нашли контейнер, то текущий контейнер и текущий индекс заносим в стек и начинаем проход по найденному контейнеру. Когда закончили проход, то из стека восстановили предыдущую итерацию - собственно контейнер и индекс с которого продолжать обход, собственно это и здесь и делается.

Только наверное еще нужно после
Код AS3:

idx = stackIdx.pop();

делать
Код AS3:

idx++

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

И
Код AS3:

var result:DisplayObject = currContainer.getChildByName(name);

стоит делать только при idx==0 - ну то есть только при первой итерации.

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

Vitalya 30.01.2012 00:06

Ув. alatar!
То есть, мы будем формировать в процессе создания сцены ассоциативный массив, и потом удобно вытягивать из него объекты.
Но это метот будет полезен в случаи включения этих событий в программу в самом начале создания сцены.
А как создать функцию которой можно будет заполнить objectsMap, считав информацию у уже существующей сцены? А именно с DisplayObjectContainer, в смысле, что у меня есть готовая сцена и мне нужно из нее вынуть именно нужный мне ОБЪЕКТ.
Заранее спасибо за ответ.
P.S Очень крутой подход c Object-ом.

Добавлено через 13 минут
Ув. Stitch512.
Все, понял как оно там все работает))

А насчет тих вопросов
Код AS3:

currContainer = stackContainers.pop();//Что здесь происходит?
idx = stackIdx.pop();//Что здесь происходит?

Это, сначала мы присваиваем последний элемент вектора currContainer-у и idx-у, а потом удаляем его же из вектора ОДНИМ ОПЕРАТОРОМ. Получается Конкатенация функций(незнал) - РЕСПЕКТ))

alatar 30.01.2012 00:23

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

Vitalya 30.01.2012 00:33

Я так понимаю, что в моей конторе, программисты используют эту функцию для упрощения работы.
Спасибо большое за ОГРОМНУЮ помощь!
P.S. Если будут варианты насчет способа сканирования сцены в objectsMap, буду рад.)

alatar 30.01.2012 00:58

И что, упрощает?

Vitalya 30.01.2012 01:15

Думаю, помогает автоматизировать доступ к каким то объектам, считывать имена из какой то структуры и что-то с ними делать.
Честно, точно не знаю - это только мои предположения)

elder_Nosferatu 30.01.2012 01:29

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

Короче, либо нормальная рекурсия, либо со старта заполняете ассоциативный масив!

wvxvw 30.01.2012 02:01

Как бы не сказать "бросайте". Но нет абсолютно ничего хорошего в том, чтобы искать экранные объекты по имени, а тем более где-то хз где. Это плохо не изза сложности алгоритма, а изза того, что код на каждом участке не должен влиять на код на другом, произвольном участке, неизвестно где находящемся. Особенность именно этого подхода еще и в том что имена - это публичные свойсва которые кто угодно откуда угодно может поменять. Они вообще существуют только потому что так, очевидно, было удобно реализовать дебаггер для Флеша (скорее всего еще для AS2). В практически любом языке есть возможности, которыми лучше не пользоваться - вот это свойство у экранных объектов как раз одно из таких. Нет, и в теории не может быть необходимости его использовать, а если нет необходимости, то противоположное справедливо - использование этого свойства = плохой код.

djyamato 30.01.2012 07:33

Observer + 2 метода у пользовательского интерфейса
Причем, Вы сможете искать какие угодно объекты, главное чтобы они реализрвали интерфейс поиска

Никаких сканирований, никаких рекурсия, зачем эта головная боль
Мне кажется все ГОРАЗДО проще
Ведь ВЫ ищите ИМЯ

интерфейс поиска
Код AS3:

package interfaces
{
        public interface ISearchableElement
        {
                function set searchName(val:String):void;
                function get searchName():String;
        }
}

Observer
Код AS3:

package managers
{
        import interfaces.ISearchableElement;
 
        public class SearchManager
        {
                private static var instance:SearchManager;
 
                protected var _elements:Vector.<ISearchableElement>=new Vector.<ISearchableElement>();
 
                public static function getInstance():SearchManager
                {
                        if (instance == null)
                        {
                                instance = new SearchManager();
                        }
                        return instance;
                }
 
                public function addSearchableElement(val:ISearchableElement):void
                {
                        _elements.push(val);
                }
 
                public function searchByName(name:String):Vector.<ISearchableElement>
                {
                        var elements:Vector.<ISearchableElement>=new Vector.<ISearchableElement>();
                        var i:int;
                        var currElement:ISearchableElement;
                        var total:int=_elements.length;
 
                        for(i=0;i<total;i++)
                        {
                                if(_elements[i].searchName==name)
                                {
                                        elements.push(_elements[i]);
                                }
                        }
                        return elements;
                }
        }
}

DisplayObject
Код AS3:

package DisplayObject
{
        import flash.display.DisplayObject;
        import flash.display.Sprite;
        import flash.events.Event;
 
        import interfaces.ISearchableElement;
 
        import managers.SearchManager;
 
        public class SearchableDisplayObject extends Sprite implements ISearchableElement
        {
                protected var _searchName:String;
 
                public function SearchableDisplayObject()
                {
                        addEventListener(Event.ADDED_TO_STAGE,addedToStageHandler);
                }
 
                public function set searchName(val:String):void
                {
                        _searchName=val;
                }
 
                public function get searchName():String
                {
                        return _searchName;
                }
 
                protected function addedToStageHandler(event:Event):void
                {
                        SearchManager.getInstance().addSearchableElement(this);
                }
        }
}

Использование
Код AS3:

package
{
        import DisplayObject.SearchableDisplayObject;
 
        import flash.display.Sprite;
 
        import interfaces.ISearchableElement;
 
        import managers.SearchManager;
 
        public class UnlimDeepDOSearch extends Sprite
        {
                protected var _searchManager:SearchManager=SearchManager.getInstance();
 
                public function UnlimDeepDOSearch()
                {
                        var mySearchableDO1:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO1.searchName="child711";
 
                        var mySearchableDO2:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO2.searchName="child2";
 
                        var mySearchableDO3:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO3.searchName="child0";
 
                        var mySearchableDO4:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO4.searchName="child1";
 
 
                        var mySearchableDO5:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO5.searchName="child711";
 
                        var mySearchableDO6:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO6.searchName="child1";
 
                        var mySearchableDO7:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO7.searchName="child14";
 
                        var mySearchableDO8:SearchableDisplayObject=new SearchableDisplayObject();
                        mySearchableDO8.searchName="child1";
 
                        (mySearchableDO5 as Sprite).addChild(mySearchableDO7);
                        (mySearchableDO5 as Sprite).addChild(mySearchableDO8);
 
                        (mySearchableDO1 as Sprite).addChild(mySearchableDO5);
                        (mySearchableDO1 as Sprite).addChild(mySearchableDO6);
 
                        addChild(mySearchableDO1);
                        addChild(mySearchableDO2);
                        addChild(mySearchableDO3);
                        addChild(mySearchableDO4);
 
 
                        var searchChild1:Vector.<ISearchableElement>=_searchManager.searchByName("child1");
                        trace("'child1' total "+searchChild1.length);
 
                        var searchChild14:Vector.<ISearchableElement>=_searchManager.searchByName("child14");
                        trace("'child14' total "+searchChild14.length);
 
                        var searchChild711:Vector.<ISearchableElement>=_searchManager.searchByName("child711");
                        trace("'child711' total "+searchChild711.length);
                }
        }
}

трэйс
Код AS1/AS2:

'child1' total 3
'child14' total 1
'child711' total 2


Vitalya 01.02.2012 14:48

Вот если кому нужно будет.
Функция поиска Объекта в контейнере без использования РЕКУРСИИ, с минимумом запусков функций(т.к функция дорогая для Flash), и с фиксированным Вектором всего на 2 элемента.

Код AS3:

public static function searchByNameDO1( owner: DisplayObjectContainer, name: String ): DisplayObject 
                {
                        trace("Вход");
                        var first:MovieClip = new MovieClip();
                        first.name = 'null';
                        first.addChild(owner);
 
                        if( owner == null ) return null;//Проверяем основной контейнер на пустоту
 
                        var stackIdx:Vector.<uint> = new Vector.<uint>(2);
 
                        var lastElVec:uint = 0;//указатель на последний элемент вектора
 
                        var currContainer:DisplayObject  = owner;
 
                        var curChildIndex:int = 0;//счетчик углубленности в контейнере
 
                        trace("curr=", currContainer.name );
                        do
                        {
                                // Имитация захода внутрь
                                if( currContainer is DisplayObjectContainer )
                                {
 
                                        trace("currContainer is DisplayObjectContainer, curChildIndex="+curChildIndex);
                                        // идем внутрь
 
                                        if( curChildIndex < (currContainer as DisplayObjectContainer).numChildren )
                                        {
 
                                                lastElVec++;//перетаскиваем указатель Вправо
                                                stackIdx[lastElVec] = curChildIndex;//stackIdx.push( curChildIndex );
 
                                                currContainer = (currContainer as DisplayObjectContainer).getChildAt( curChildIndex );
 
                                                curChildIndex = 0;
                                                trace("идем внутрь", currContainer.name );
                                                continue;
                                        }
                                }
                                // проверка на доститжение результата
                                trace( "проверка на доститжение результата" );
                                if( currContainer.name == name ) return currContainer;
 
                                // Идем наверх
 
                                // выше некуда
                                if( stackIdx.length == 0 )
                                        return null;
 
                                // выше есть куда
                                trace("выше есть куда до:",curChildIndex);
                                //curChildIndex = stackIdx.pop()+1;
                                curChildIndex = stackIdx[lastElVec];
                                lastElVec--;
                                curChildIndex++;
 
                                trace("выше есть куда после:",curChildIndex);
                                currContainer = currContainer.parent;//возвращаемся к родителю
 
                                // Если мы поднялись до уровня вызова
                        } while ( currContainer != first );
 
                        return null;       
                }


alatar 01.02.2012 14:52

Код AS3:

first.addChild(owner);
 
if( owner == null ) return null;//Проверяем основной контейнер на пустоту

Не поздновато, проверять то? :)

wvxvw 01.02.2012 15:33

Ну только вектор никак не длины 2, а ровно столько, сколько максимальная вложенность.

Vitalya 01.02.2012 15:42

Вектор фиксированный, 2 элемента.
Код AS3:

first.addChild(owner);
 
if( owner == null ) return null;//Проверяем основной контейнер на пустоту

поздно было бы проверять это внутри цикла)

in4core 01.02.2012 16:12

Vitalya вы разве не понимаете, что first.addChild(null); выкенет RTE?

alatar 01.02.2012 17:06

Цитата:

Вектор фиксированный, 2 элемента.
Фиксированный он будет, если вы вторым параметром в конструктор передадите true.
Код AS3:

// выше некуда
if( stackIdx.length == 0 )
        return null;

Вы его нигде не уменьшаете, у него длинна никогда не станет 0.

wvxvw 01.02.2012 17:12

>> Вектор фиксированный, 2 элемента.
Поменяйте вот эту строчку и создайте такой список объектов отображения, чтобы глубина была больше 2
Код AS3:

trace("идем внутрь", currContainer.name, lastElVec);


Vitalya 01.02.2012 19:44

Цитата:

Фиксированный он будет, если вы вторым параметром в конструктор передадите true.
Код AS3:
// выше некуда
if( stackIdx.length == 0 )
return null;
Вы его нигде не уменьшаете, у него длинна никогда не станет 0.
Это верно!))Я просто забыл убрать из реализации с динамическим вектором.

Насчет фиксированного Вектора. Второй параметр в конструкторе дает ОШИБКУ((
Код AS3:

var stackIdx:Vector.<uint> = new Vector.<uint>(300, true);

Добавлено через 1 минуту
Я так понял, что если без второго параметра true в векторе, то он будет увеличиваться только в случае переполнения???

wvxvw 01.02.2012 20:03

Да, вроде того. Сначала создаст пустой вектор на 256 элементов, а потом будет прибавлять по 256 * х, где х с каждым разом удваивается. Или что-то в этом духе. Это менялось от версии к версии плеера.
Конструктор вектора всегда принимал 2 аргумента, с самой первой версии. Вы наверное забыли new - в таком случае это конвертация в вектор, а не вызов конструктора.
Не нужно писать три и больше вопросительных знаков - "???" в русском языке это эвфемизм "какого ...?" Если вы хотели задать вопрос, а не обматерить собеседника, одного вопросительного знака как раз достаточно.

Vitalya 07.02.2012 02:21

В моем случаи больше одного знака "?" - значит что очень нужен ответ и побыстрее. Никого ничем не хотел обидеть!)


Часовой пояс GMT +4, время: 10:56.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.