1. ActionScript / Говнокод #12842

    −90

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    /**
     * better,faster OLD SCHOOL HACKS ^^
     * **/
    public static function compareObject(obj1:Object, obj2:Object):Boolean {
    	var buffer1:ByteArray = new ByteArray();
    	buffer1.writeObject(obj1);
    	var buffer2:ByteArray = new ByteArray();
    	buffer2.writeObject(obj2);
    
    	// compare the lengths
    	var size:uint = buffer1.length;
    	if (buffer1.length == buffer2.length) {
    		buffer1.position = 0;
    		buffer2.position = 0;
    
    		// then the bits
    		while (buffer1.position < size) {
    			var v1:int = buffer1.readByte();
    			if (v1 != buffer2.readByte()) {
    				return false;
    			}
    		}
    		return true;
    	}
    	return false;
    }

    Сравнение объектов сериализацией, по моему такого тут еще не было...

    Запостил: kostoprav, 02 Апреля 2013

    Комментарии (58) RSS

    • Производительненько.
      Ответить
    • На самом деле это не такое уж и плохое решение сразу по нескольким причинам:
      1. Циклические графы свойств делают обход и сравнение нетривиальной задачей.
      2. Сравнивать не public свойства - как правило мало толку.
      3. Такой метод почти наверняка используется в контексте, где задействована сериализация, и объекты произвольных типов нужно сравнивать по значению.
      4. Это дает возможность при удачной реализации IExternalizable сделать эту проверку гораздо более быстрой, чем цикл с обходом всех свойств.
      5. [Transient] опять же позволяет исключить какие-то свойства из такого сравнения.
      6. Опять же, способ сравнить всяких наследников Proxy в которых перебор свойств может вообще быть бессмысленной задачей.
      7. Можно сравнить объекты разных типов (если, например, сериализация настроена так, что на выходе получаем XML), то таким образом можно сравнить с XML.

      Эта тема давно и неоднократно поднималась на флешере... Вобщем, не говнокод.
      Ответить
      • Придется отписаться.
        >faster
        > OLD SCHOOL HACKS ^^
        >var v1:int = buffer1.readByte();
        1.Что это за хуйня? Человек претедует на почётное звание байтоёба. А читает в инт и сравнивает байтами. Ёбанный стыд.

        var v1:int = buffer1.readInt(); if (v1 != buffer2.readInt()) {
        И теоретически наш код ускоряется в 4 раза.

        2.Для начала, ради приличия делают элементарные, детские проверки - на null, на идентичность типов, итд.

        В общем, говнокод.

        PS. Против самого способа сравнения ничего не имею против.
        Ответить
        • C# по readInt читает byte и -1, если конец файла
          Ответить
        • > В общем, говнокод.
          Нет, лёгкая небрежность, не криминал.
          Ответить
        • Реализация херовая, но задумка годная. Ну или как-то так. Можно было конечно лучше сделать.

          По реалиям флеша самый быстрый вариант будет bufferA[i] === bufferB[i], т.как вызов функции получается сильно накладным (или можно использовать какой-нибудь пост-процессор, который этот массив представит как DomainMemory, и тогда можно будет использовать инструкции быстрого доступа и читать по 4 байта за раз. Но скорее всего байтов там совсем не много получается. Двузначное число и очень редко трехзначное. Так что не стоит.
          Ответить
          • >По реалиям флеша самый быстрый вариант будет bufferA[i] === bufferB[i]
            Вам виднее.
            >Реализация херовая, но задумка годная
            Не спорю.
            >Лёгкая небрежность, не криминал.
            Да если б не коммент faster OLD SCHOOL HACKS не плюсанул бы.
            Ответить
      • > 2. Сравнивать не public свойства - как правило мало толку.
        чисто так - private/protected поля тоже сериализуются

        > 4. Это дает возможность при удачной реализации IExternalizable сделать эту проверку гораздо более быстрой, чем цикл с обходом всех свойств.
        а выделение памяти под буфера нынче бесплатное?

        > 5. [Transient] опять же позволяет исключить какие-то свойства из такого сравнения.
        а еще можно написать нечто компараторо-подобное, и так же исключить

        > 7. Можно сравнить объекты разных типов (если, например, сериализация настроена так, что на выходе получаем XML), то таким образом можно сравнить с XML.
        А во что сериализуется XML, полученный на входе?
        Ответить
        • Ох боже... памяти на этот буффер выделится аж просто немеряно. Скорее всего одно какое-нибудь событие типа MouseEvent его перекроет по количеству задействованой памяти. Нашли тоже, где экономить. Кроме того - ну выделится, и сразу же удалится - мусорщик во флеше неплохой относительно, так что тут жаловаться - вообще бессмысленно.
          Про XML вы не поняли: у вас есть XML полученный откуда-то и ваш объект, и вам нужно сравнить XML с этим объектом. Задача возникает очень часто, если нужно показать какие-нибудь данные, которые часто обновляются, и получаются из внешнего источника.

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

          Как пример: сравниваем два таких объекта:

          class A {
          public function get foo() { return [1, 2, 3]; }
          }
          new A().foo == new A().foo; // как бы не так

          Ну и это не отменяет проблем с Proxy и наследниками, да и вообще со всякими объектами, к которым уже доступа нет их поменять.
          Ответить
          • Особенно быстро выделяется память в момент GC.

            > Ох боже... памяти на этот буффер выделится аж просто немеряно.
            А это уже зависит от глубины вложенности полей. Сериализация одного объекта может утянуть за собой граф из 1000 привязанных.

            > Реализовать Comparator - ага, успехов вам в этом нелегком. Только сначала потрудитесь выяснить сколько головной боли это стоило, например, в той же Яве, и как долго (да, вобщем-то и до сих пор) это было причиной разного рода непредвиденных ошибок. Вы тут одной реализацией не отделаетесь, т.как нужно будет очень много чего предусмотреть.

            Intellij IDEA -> Alt-Insert -> hashCode() and equals() помогает в 99% случаев.

            В вашем же примере вы сравниваете ССЫЛКИ. Они как ни странно - разные. В Java есть Arrays.equals() для этого случая.
            Ответить
            • 1000 объектов (при том, что это явно очень редкий случай) - ну, получится у вас 10 Килобайт буфер? - офигеть. Это по размерам примерно как иконка с крестиком рядом с сообщением.
              Ответить
              • Даже 5 байт выделяются не бесплатно. В то время, как может быть достаточно загрузить на стек виртуалки 2 адреса и сравнить, вообще ничего не выделяя.
                Ответить
              • А вообще... Понимаю, почему все as-программисты жалуются, что все тормозит. Аллокация 2*10Кб на сравнение 2 объектов - это приемлемое решение? А что будет, если это в цикле вызывать, бегая по массиву?

                http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html#toJSON() - кстати, тоже можно заюзать наверное.
                Ответить
                • С JSON скорее всего будет медленее. Кроме того JSON - плохая сериализация для AS объектов. Мы получим все те же проблемы с циклическими графами + потерю / лишние данные при сериализации + JSON как раз таки займет гораздо больше памяти (по обстоятельствам конечно, но скорее всего в десятки, а может и в сотни раз больше), чем тот же AMF.

                  AS программисты жалуются на то, что аллокатор медленно работает? А Явисты не? Да даже те, кто пишут на Си - тоже жалуются. Жаловаться - это профессиональная привычка программистов, от языка практически не зависит.
                  Ответить
        • >а выделение памяти под буфера нынче бесплатное?
          Полагаю если адобе не мудаки, то должны были сделать его ленивым, как потоки.
          Если же вывод в примере не идет в потокообразный объект - тогда говно, да.
          Ответить
          • Ну естественно, там поток, который растет. Вначале Х байт, потом больше. Но это все равно создание нового объекта + выделение памяти под буфер внутри него. Это все же не 2 байта переслать.
            Ответить
            • То есть он сначала должен полностью высрать джва сериализованных объекта (по 100 метров, например) в buffer1 и buffer2.
              И только потом только сравнить их?
              Безо всякой асинхронности?
              Ответить
              • Да, именно так.
                Ответить
                • Тогда сей код - полное говно, использовать которое можно строго в тестовых целях.
                  Ответить
          • Там никогда не будет мегабайтов. Рантайм умрет быстрее, чем вы столько насериализируете. Ну или нужно будет в несколько заходов делать. Или специально сериализацию такой сделать, чтобы она писала много лишнего.
            Ответить
            • А почему вообще сравнение 2 объектов должно требовать сериализации? Я в джаве пока что обхожусь...
              Ответить
              • Вы не поняли вообще зачем код был написан... Вы не сравниваете указатели, вы сравниваете значения. Вам так или иначе нужно привести значения к какой-то нормальной форме, чтобы их сравнить.
                Вы собираетесь создавать equals / getHashCode - для всех встроенных объектов? Как, если не секрет? А их есть несколько сотен разновидностей. А к ним в придачу библиотечные классы, которых может быть тысячи. Откуда у вас возьмется столько рвения и терпения их всех написать?
                А написать equals, например, для циклических связных списков?
                Или для объектов, которые уже реализовали equals, но не так, как вам нужно.
                А как вы будете сравнивать представителей класса с представителями его подклассов, и наоборот?

                Более того, такая функция скорее всего могла понадобиться для работы с объектами, которые попадают уже в сериализованом виде, и их нужно сравнить с уже имеющимися, чтобы понять нужно ли делать замену, или нет. Такая ситуация часто возникает при работе с флексовыми сервисами типа Блейза, где почти все объекты которыe передаются с сообщениями наследники / экземпляры ObjectProxy - для которого, уж поверьте, очень нетривиально написать equals который бы хоть что-то вменяемое делал.
                Ответить
                • > Вам так или иначе нужно привести значения к какой-то нормальной форме, чтобы их сравнить.

                  Я могу просто сравнить свойства.

                  > Вы собираетесь создавать equals / getHashCode - для всех встроенных объектов? Как, если не секрет? А их есть несколько сотен разновидностей. А к ним в придачу библиотечные классы, которых может быть тысячи. Откуда у вас возьмется столько рвения и терпения их всех написать?

                  Я думаю, встроенные классы не тупые, и умеют сравниваться. Ну это было бы логично.

                  > А написать equals, например, для циклических связных списков?

                  for each? не, не слышал...

                  > А как вы будете сравнивать представителей класса с представителями его подклассов, и наоборот?

                  а как тут сериализация поможет? В сериализованном виде дескриптор БУДЕТ отличаться для родителя и потомка - фейл.

                  > Такая ситуация часто возникает при работе с флексовыми сервисами типа Блейза, где почти все объекты который передаются с сообщениями наследники / экземпляры

                  И?
                  Ответить
                  • Вот жеж... нет, вы не можете просто сравнить свойства. См. пример выше. Свойства могут иметь значения (и очень часто таки имеют такие значения), которые вы "просто" сравнить не можете.
                    Являются ли массивы [1, 2] и [1, 2] одинаковыми? А если x = [1, 2] x[x] = x и y = [1, 2], одинаковы ли x и y? А если это словарь, имеет ли значение тот факт, что ключ в нем равен 1 или "1"? А как вы об этом узнаете?

                    Вы "думаете" что встроенные классы умеют сравниваться, а в справку не заглянули, но тем не менее спорите?

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

                    Сорри, вы либо совсем новичек в этом языке, либо его вообще не знаете. Лучше начните со справки, а потом приступайте к обвинениям.
                    Ответить
                    • > Сериализация для классов, которые не декларировали registerClassAlias будет делаться так, как будто сериализуется обычный объект, поэтому, если значения полей совпадут, то и будут равны, не важно в какой степени родства находятся.

                      Зачем нужны такие классы? Это отказ от ООП какой-то, точнее переход на HashMap-oriented OOP.

                      > Являются ли массивы [1, 2] и [1, 2] одинаковыми?

                      Да, есть такой цикл - for each называется.

                      > А если это словарь, имеет ли значение тот факт, что ключ в нем равен 1 или "1"? А как вы об этом узнаете?

                      Имеет. В 1 случае ключом является число, в другом строка. Почитайте про типизацию, это интересная тема. Хотя судя по комментам, передо мной выходец из PHP и поклонник HashMap-oriented OOP

                      > Вот жеж... нет, вы не можете просто сравнить свойства. См. пример выше. Свойства могут иметь значения (и очень часто таки имеют такие значения), которые вы "просто" сравнить не можете.

                      Добавил в цитатник
                      Ответить
                      • Полиморфизм, энкапсуляция, наследование, сериализация? - чет я совсем отстал от новых веяний в ООП.
                        А по поводу словаря - ну, хорошо, это вы сейчас так решили. А что будет завтра, когда вы решите по-другому? А что, если за вас уже кто-то решил, по-другому? Предположим, узнать есть ли в словаре два разных ключа "1" и 1 не сериализируя его вы вообще совсем никак не сможете. Это скорее недостаток в реализации словаря как такового, но тем не менее, так оно работает и с этим жить.
                        О, я знаменит, меня теперь начинающие цитируют!
                        Ответить
                        • > энкапсуляция

                          Чего? Я про инкапсуляцию слышал.

                          > сериализация

                          В мое время там была абстракция.... Хотя наверное

                          "чет я совсем отстал от новых веяний в ООП."
                          Ответить
                        • Тому, как правильно писать equals в любой книжке по жабке обычно посвящена отдельная глава. В Java, кстати, объекты сравниваются с объектами, т. е. тип того, с чем сравниваешь, заранее неизвестен. То бишь при переходе в динамику мало что изменится.

                          По поводу классов и подклассов - всем будет жить гораздо проще, если base.equals(derived) и derived.equals(base) всегда будут возвращать false. Это правильно в 99.999% случаев, т.е. когда при наследовании не добавляются новые поля. Иначе сложно обеспечить симметрию и транзитивность equals.
                          Ответить
                          • Генераторы equals()/hashCode() в современных IDE спрашивают Accept Derived Classes as Equal или что-то в этом духе :)

                            А так:
                            @Override
                            public boolean equals(Object o) {
                                if (this == o) return true;
                                if (o == null || getClass() != o.getClass()) return false;
                            
                                ...
                            }
                            Ответить
                          • Так тут же как раз это и не требуется. Нужно понимать контекст и назначение этой функции.
                            Ситуация, как правило, следующая: показываем очень быстро обновляющуюся таблицу, например биржевые сводки курсов валют, или что-то в этом духе. Данные в таблице обновляются не все одновременно, а что пришло - то и обновили. Чтобы выяснить, нужно ли обновлять строчку / столбец нужно выяснить имеет ли это практический смысл (т.е. изменится ли что либо с точки зрения пользователя), иначе изза тяжеловесного интерфейса будут тормоза. И в этом случае настоящие - а не выдуманные типа тех, которые могут быть вызваны паузами мусорщика.
                            Что до сравнений - в контексте этой задачи, как правило не нужно принимать во внимание родство между классами, т.как это чаще всего техническая деталь, которая не имеет отношения к задаче - не факт, что все объекты с одним и тем же значением были получены одинаковым способом. Иногда это не возможно по сугубо техническим причинам, например - закрыто наследование от какого-то встроенного класса.

                            Не смотря на то, что в книжках по Яве этому уделяется глава - этот аспект, как правило создает много головной боли, и тем больше, чем менее тривиальный объект. Что касается разницы с АС, то особенным моментом являются прокси объекты, которые могут управлять перечислением своих свойств (могут их даже создавать и удалять на ходу), могут неадкватно (с точки зрения сравнения) реагировать на вопрос "а есть ли свойство", "а есть ли еще свойства" и "а равно ли свойство чему-то". Ну и кроме того, свойства в АС - могут быть сложными функциями, а ключи могут быть и не строками вовсе, и в таком случае получение списка свойств - это очень заморочливая процедура.
                            Ответить
                          • ЗЫ. Когда child.equals(parent) = false при том, что parent.equals(parent) = true - это как бы Лисков не одобряет. ;)
                            Ответить
                            • > Лисков не одобряет
                              С чего это вдруг? Если бы наследники должны были возвращать одинаковые с базой выходы для одинаковых входах, смысла в наследовании было бы мало.
                              Лисков требует отсутствие нарушения семантики и контрактов при наследовании, и с equals тут всё совершенно прекрасно, семантика чётко соблюдена.
                              Ответить
                        • >Полиморфизм, энкапсуляция, наследование, сериализация? - чет я совсем отстал от новых веяний в ООП.
                          Ну кто-то недавно с пеной у рта рассказывал про "неправильное ООП в js", и что там "нет контроля за типизацией" а теперь же наоборот отстаивает нетипизированное, динамическое сравнение.

                          > переход на HashMap-oriented OOP
                          Вот-вот. Это ж суть js.
                          Ответить
                          • > HashMap-oriented OOP
                            А в лиспе, похоже, Tuple oriented OOP
                            Ответить
                          • А так вы тоже нашли связь между сериализаций о ООП?
                            Это, кстати может оказаться интересной темов в, например, vObj исчислении. Вполне потянет на статью, если даже не тему для исследовательской работы.
                            Ответить
                    • >Вы "думаете" что встроенные классы умеют сравниваться
                      Флешепроблемы. Или же вы просто не умеете их сравнивать.

                      В общем не буду читать эти простыни, а отплюсую @костоправа.
                      Ответить
                      • Смотрите, как бы результат-то какой:
                        - вы не пишете на АС, поэтому вам все равно как оно будет, или не будет работать. Ну и ваше мнение не то чтобы совсем высосано из пальца, но опирается на реалии совсем другого языка, которых просто нет в этом контексте.
                        - костоправ - очень самонадеяный Ява-писатель, который по неопытности набрел на какой-то код, который не понял, но решил, что это плохо, потому что в Яве это можно сделать по-другому.

                        Что получится врезультате: скорее всего костоправ запилит копию явовских equals, потом будет очень долго воевать с существующей системой, которая в его мирок никак не будет укладываться. А потом, скорее всего бросит это дело / локально пофиксит несколько явных проблем. Потом перейдет в другой отдел и будет с ужасом вспоминать о проделаной работе.

                        Хуже всех будет тем, кому достанется в поддержку задел оставленный костоправом, на поддержку / переделку.

                        Случалось много раз.
                        Ответить
                        • Замените в вашем тексте Java -> AS, костоправ -> wvxvw.

                          Будет больше похоже на правду.
                          Ответить
                          • Да вы прям с такой аггресией это воспринимаете... я же не со зла :) Ну вы чего-то не поняли / не знали / не подумали - с кем не бывает. Кроме того, есть такое ощущение, что вы прям так плохо себя чувствует за пределами уютного и теплого мира Явы - вам бы поскорее обратно, где все радует глаз, понятно и правильно. Зачем вы вообще потянулись заниматься вещами, которые вам не нравятся?
                            Ответить
                            • Нет, просто я после такого wvxvw (точнее автора сего говна) оптимизировал Flex-клиент игры. Так что не надо считать, что я не знаю AS. Я работаю и с AS и с Java. Но почему-то джависты в разы более адекватны и любят типизацию. А вот аскриптеры знают, что она есть, знают, что это хорошо - но не используют. Ибо есть динамика.
                              Ответить
                              • Бесспорно, в подавляющей массе явисты, как минимум обладают профильным высшим образованием, и вцелом, более грамотные программисты чем флешеры. Но в данном случае вы просто не правы, и не правы потому, что меряете неправильными мерками.
                                Если эту функцию использовали там, где можно было сравнить ссылки - тогда бы ее удаление / замена была бы оптимизацией, а если нет - вероятность того, что вы что-то существенно улучшили бы заменив ее equals стремится к нулю.

                                Но вот ваши выводы про типизацию: у Явистов как бы и альтернативы никакой нет... любят не любят - в Яве никакой динамической типизации не будет в обозримом будущем. В АС есть разные подходы, и, хоть я и не сторонник, но иногда потеря типизации оправдана. Как аргумент в защиту: это сокращает код. Делает его более понятным убирая технические детали. Позволяет делать код более модульным, совместимым с будущими добавками и изменениями.
                                Флексовый фреймворк, с другой стороны, написан явистами, которые плохо знали АС, и поэтому он такой какой он есть... с массой неидеоматического кода, заимствоваными бесполезными идеями и условностями.
                                Ответить
                                • В 99% мест этот код использовался именно для сравнения ссылок.

                                  > Как аргумент в защиту: это сокращает код. Делает его более понятным убирая технические детали. Позволяет делать код более модульным, совместимым с будущими добавками и изменениями.

                                  Особенно потом легко разбираться, что в какой переменной лежит - массив, объект, а может быть вообще число. Бесспорно, легко добавлять новый функционал.
                                  Ответить
                                  • Вы не поверите: да, не сложно разобраться. Да, есть много языков, где так именно и пишут, вот только небольшой список тех, о которых я знаю:
                                    Скала (так же известная как JML),
                                    Питон,
                                    Эрланг,
                                    Камл,
                                    В Лиспе аннотации типов опциональные и их используют только в случае необходимости оптимизации,
                                    Руби,
                                    Смолток (Стронгток - опциональный довесок),
                                    ПХП,
                                    ж. скрипт,
                                    Хекс (опциональное, как в Скале, Камле или Лиспе) - но об этом вы наверное не слышали.
                                    Я так подозреваю, что Перл и Луа - так же устроены, но не пользовался ими никогда.
                                    Этим исчерпываются мои познания, но языков таких много и на них написаны иногда очень большие приложения в сотни тысяч строк.
                                    Ответить
                                    • Что в этом списке делает Scala? Там типы всех переменных известны заранее, только автовывод работает.
                                      Ответить
                                      • В Лиспе типы всех переменных известны заранее тоже. В Эрланге - тоже. Более того, в том же АС,даже если вы их не указываете, они тоже известны заранее. Речь идет об исходном коде, а не о том, когда и что проверяет типы.
                                        Ответить
                                    • Да и OCaml здесь тоже явно лишний.
                                      Ответить
                                      • А, о, совсем забыл про самый лучший ЯП: Хаскел - в нем тоже можно не писать, и все всем всегда понятно. Там просто непонятно не бывает.
                                        Ответить
                                        • Сарказм?
                                          Ответить
                                          • О. А я только хотел тебя сюда призвать.
                                            Ответить
                                          • http://en.wikipedia.org/wiki/Categorial_grammar

                                            Ключ к разгадке: просто выводим типы, пишем их рядом со словами - и все становится на свои места.
                                            Ответить
                                    • http://en.wikipedia.org/wiki/Lua_(programming_language)

                                      Lua подобно AS поддерживает разные парадигмы типизации.
                                      Ответить
                                    • На самом деле, существует очень мало мест, где подобная типизация реально помогает, и не привносит дополнительные проблемы.
                                      Ответить
    • Я хочу взять к себе на работу того, кто это сделал.
      Ответить
    • Укопипастила.
      Ответить
      • Вот именно поэтому к флешерам такое отношение
        http://govnokod.ru/12741#comment172084
        Ответить
        • Это абсолютно нормальный хоть и не оптимальный код. as типизированный по желанию и позывам здравого смысла. Часто задачи полной типизации не требуют, мы не становимся в позу при этом "задача неправильная, сервер говно отдает" а делаем как удобно. К тому же as как правило используется для отрисовки чего либо и это 80 - 99% ресурсов жрет, так что смысла в байтоебстве просто нету. Ну задачи у языка такие.
          Ответить
    • Имхо, говно в названии функции. Что должно вернуть "сравнить объекты"?
      Насколько интуитивен код
      if compareObjects(o1, o2) { // put your code here
      ?

      А, так там вообще compareObjects
      Ответить

    Добавить комментарий