Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 1.0/2.0 (http://www.flasher.ru/forum/forumdisplay.php?f=93)
-   -   Функции внутри функций - почему это зло? (http://www.flasher.ru/forum/showthread.php?t=117508)

dashaaa 29.10.2008 12:47

Функции внутри функций - почему это зло?
 
Добрый день,
извините за назойливость, но мне необходимо понять - почему плохо создавать функции внутри функций?
И как правильно организовать следующий код?
Код:

public function loadImg():Void {
        var mclLoader:MovieClipLoader = new MovieClipLoader();
        var oLoadListener:Object = new Object();
        mclLoader.addListener(oLoadListener);

        function onLoadError(mcTarget:MovieClip, sErrorCode:String):Void {}
}

Поиск довольно внятно ответил мне, что это "*****код", но хочется обоснования...:wacko:

iNils 29.10.2008 13:08

Код:

class имя класса {
        var mclLoader:MovieClipLoader = new MovieClipLoader ();

        public function конструктор класса () {
                mclLoader.addListener(this);
        }

        public function loadImg():Void {

        }

        private function onLoadError (mcTarget:MovieClip, sErrorCode:String):Void {
        }
}


etc 29.10.2008 13:13

dashaaa, ну во-первых, приведенный вами код не будет работать, просто потому что onLoadError не принадлежит объекту oLoadListener. А вложенные функции плохи тем, что во первых, плывет область видимости и начинаются проблемы с обращением к различным свойствам и объектам, во вторых, вместо того, что создать метод один раз в памяти, он постоянно пересоздается новый.

Wolsh 29.10.2008 13:16

Дело в том, что функция просто выполняется при вызове, и выполнившись, возвращает результат или меняет чтото вне себя. После того, как она выполнилась, все ее внутренние переменные больше недоступны, точнее их уже нет. Все эти переменные - mclLoader, oLoadListener, и функция onLoadError() существуют лишь до тех пор, пока выполняется loadImg(), потому что это ее внутренние переменные. Они как пассажиры пролетающего вагона - могут разговаривать друг с другом, но Вы, на полустаночке), ни словом с ними не перекинетесь. Пролетая мимо, они Вам могут крикнуть "загрузи картинку и отдай Васе на пятом сиденье", но пока Вы сбегаете за картинкой - ни Васи, ни сиденья.
(из моего поста могло создаться впечатление, что дело во времени, но на самом деле конечно в областях видимости - т.е. нормальная организация подразумевает, что Вася стоит с Вами на полустаночке. Т.е. функция не должна быть внутри другой функции)

dashaaa 29.10.2008 14:03

Цитата:

Код:

class имя класса {
        var mclLoader:MovieClipLoader = new MovieClipLoader ();

        public function конструктор класса () {
                mclLoader.addListener(this);
        }

        public function loadImg():Void {

        }

        private function onLoadError (mcTarget:MovieClip, sErrorCode:String):Void {
        }
}


Compiler Error, line 2 - A class's instance variables may only be initialized to compile-time constant expressions.

Но вообще-то идея ясна, спасибо.

__etc, спасибо, с областью видимости уже столкнулась, и то, что метод пересоздаётся - это аргумент.
Wolsh, спасибо, понятно =)

Mnilionic 29.10.2008 20:42

создавайте экземпляр MovieClipLoader в конструкторе класса

chingachgoog 29.10.2008 21:28

Функция внутри функции - это нормальное явление, просто надо знать для чего это нужно.
То, что "поезд уходит" тоже не совсем верно:
Код:

function A(){
        var x="123"
        B()
        function B(){
                link=this
                this.x
        }
}
A()

trace(link.x)

А вообще посмотрите тему Битва гигантов..., там как раз ближе к концу объекты активации исследуются.

Mnilionic 29.10.2008 21:35

хы-хы
в делигаторе функция внутри функции, но там правда ссылка на неё живая

Division 29.10.2008 21:47

Цитата:

Функция внутри функции - это нормальное явление, просто надо знать для чего это нужно.
А для чего нужно, если не секрет? Мне вообще кажется что это довольно удобно в некоторых случаях. Например, если большая функци выполняет много однотипных действий, их можно вынести во вложенную функцию. ИМХО.

iNils 29.10.2008 22:54

Цитата:

А для чего нужно, если не секрет?
Только для реализации Delegate. Других оправданных примеров использования я не вижу.
Цитата:

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

Wolsh 30.10.2008 00:09

chingachgoog, мы с Вами как-то спорили про клипы и кнопки, так вот это примерно из той же серии. Когда человек чётко представляет что и как работает, он безусловно просто выберет действительно оптимальный вариант для решения своей задачи. Например, создаст ядерное оружие вместо электростанции))) Правда при этом другие будут на него косо смотреть, примерно как я смотрел бы на коллегу, принёсшего мне такое вот рэндзю из активаций. Почти всегда можно найти способ, в народе именуемый хаком, и взломать головы своим соратникам, пытающимся где-нибудь в 2659-й строке понять, откуда что взялось. А можно просто сделать так, как удобно, понятно и предсказуемо. Хитрые головоломные ходы должны использоваться только там, где без них никак. Делегате - да, отличный пример такой необходимости. Для упрощения этой необходимости он и создан))))

chingachgoog 30.10.2008 02:15

Область видимости никуда "не плывет" и почему должна "расходоваться память"? Просто область видимости иная (собственно это и причина применения) и память занята функцией временно.
Но механизм действия функции в функции очень сложен, придумать для чего это надо - не просто. Потому распространен стереотип - "это зло", "*****код" и т.д. А это просто еще одна из редко используемых возможностей флеша, к тому же, мало кому понятная.
PS Попытался вспомнить когда я такую конструкцию применял на практике - не получилось :)

iNils 30.10.2008 02:52

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

chingachgoog 30.10.2008 09:42

iNils, я уловил аналогию между словами "*****код" и "гадить в подъезде", но не между "гадить в подъезде" и "вложенные функции".
Да, вложенные функции не есть "ясное написание кода, которые не вызывает проблем". Но разве, например, умножение на двое побитовым сдвигом - это "ясное написание кода, которое не вызывает проблем"? В таком случае умножение побитовым сдвигом - это "гадить в подъезде", т.к. есть "специально отведенное место" - простая запись "*2".

iNils 30.10.2008 10:55

chingachgoog, а при чем тут побитовый сдвиг? Это как школьник, которого мапа ругает за двойку, начинает оправдываться словами "А вот у Васи родители его за двойку не ругают". Не подтягивать в качестве примера то, о чем речь и не заходила, вынуждая нас оправдываться за кого-то там, кто так делает и тут вообще не присутствует.

Побитовый сдвиг используется тогда, когда нужно думать именно категорией сдвига, а не умножения. Как пример, работа с RGB и его составляющими.

†‡Paladin‡† 30.10.2008 12:20

Функции в функции не считаю чем-то из ряда вон. В некоторых случаях такая реализация только улучшит читаемость кода.

http://habrahabr.ru/blogs/webdev/38642/ - советую прочитать про то, как это используется в javascript`е

chingachgoog 30.10.2008 12:44

Речь зашла о "ясном написание кода, которое не вызывает проблем". И тут побитовый сдвиг (примененный, скажем для увеличения производительности расчета, по сравнению с простым *2) никакой ясности не внесет и запросто вызовет проблемы. Ну и что? Применять побитовый сдвиг только при работе с RGB?

Нельзя Васи ставить двойку за то, что он решил задачу способом иным, чем предполагал учитель:

Цитата:

Сэр Эрнеcт Резерфорд, президент Королевской академии и лауреат Нобелевской премии по физике рассказывал такую историю:

Однажды к нему обратился коллега за помощью. Он собирался поставить самую низкую оценку по физике одному из своих студентов, в то время как тот утверждал, что заслуживает высшего балла. Оба — преподаватель и студент — согласились положиться на суждение третьего лица, незаинтересованного арбитра. Выбор пал на Резерфорда. Экзаменационный вопрос гласил: «Объясните, каким образом можно измерить высоту здания с помощью барометра?».

Ответ студента был таким: «Нужно подняться с барометром на крышу здания, спустить барометр вниз на длинной верёвке, а затем втянуть его обратно и измерить длину верёвки, которая и покажет точную высоту здания».
Случай был и впрямь сложный, так как ответ был абсолютно полным и верным! С другой стороны, экзамен был по физике, а ответ имел мало общего с применением знаний в этой области.

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

Заинтересовавшись, Резерфорд попросил молодого человека приступить к ответу, не дожидаясь истечения отведённого срока. Новый ответ на вопрос гласил: «Поднимитесь с барометром на крышу и бросьте его вниз, замеряя время падения. Затем, используя формулу, вычислите высоту здания».

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

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

— Неплохо, — сказал Резерфорд. — Есть и другие способы?

— Да. Есть очень простой способ, который, уверен, вам понравится. Вы берёте барометр в руки и поднимаетесь по лестнице, прикладывая барометр к стене и делая отметки. Сосчитав количество этих отметок и умножив его на размер барометра, вы получите высоту здания. Вполне очевидный метод.

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

— Наконец, — заключил он, — среди множества прочих способов решения данной проблемы лучшим, пожалуй, является такой: возьмите барометр с собой, найдите управляющего и скажите ему: «Господин управляющий, у меня есть замечательный барометр. Он ваш, если вы скажете мне высоту этого здания».

Тут Резерфорд спросил студента, неужели он действительно не знал общепринятого решения этой задачи. Он признался, что знал, но сказал при этом, что сыт по горло школой и колледжем, где учителя навязывают ученикам свой способ мышления.

Студент этот был Нильс Бор (1885–1962), датский физик, лауреат Нобелевской премии 1922 г.

iNils 30.10.2008 13:09

Цитата:

В некоторых случаях такая реализация только улучшит читаемость кода.
В каких?
Цитата:

Сообщение от chingachgoog (Сообщение 773769)
Нельзя Васи ставить двойку за то, что он решил задачу способом иным, чем предполагал учитель:

Слышу звон, да не знаю где он. Цитировать надо к месту, по смыслу, а не по общему слову "двойка".

chingachgoog 30.10.2008 13:18

Цитата:

Сообщение от iNils (Сообщение 773773)
Слышу звон, да не знаю где он.

Когда я слышу звон типа "*****код", "быдлокод" (возможны вариации), то скороее всего звонарь либо очень высокого мнения о себе и судит по своим стереотипам (типа как учитель Бора), либо просто не понимает смысла кода (очень характерно для вложенных функций и связанного с этим объектов активации).

add:
Цитата:

Сообщение от iNils
Цитировать надо к месту, по смыслу, а не по общему слову "двойка".

Там не было цитаты - это просто ассоциация :)

iNils 30.10.2008 13:38

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

Wolsh 30.10.2008 14:18

Не, ну если измерять высоту здания веревкой с привязанным грузом - пример незакостенелости мышления в пику показаниям барометра - то да, дошли до демагогии и я самоудаляюсь. chingachgoog, Вы даже не поняли, что в своей борьбе за свободу индейцев привели пример самого настоящего быдлокода, и то что его автор - Нильс Бор - отнюдь не делает чести ни "коду" ни Бору. Если бы товарищ Бор в этой ситуации предложил НОВЫЙ метод, тогда да, можно поговорить о незакостенелости. А все эти "методы" оба физика прекрасно знали, просто не были таким быдлом, чтоб забивать микроскопом гвозди. В следующий раз для демагогии выбирайте примеры поудачней.

iNils 30.10.2008 14:24

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

chingachgoog 30.10.2008 15:09

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

iNils 30.10.2008 15:13

Пример где?

Wolsh 30.10.2008 15:32

OK. Применение замыканий (closure), упомянутое †‡Paladin‡†, иногда способно улучшить читабельность. Пример два года висит в моей подписи))) Думаю, никто не будет спорить что запись
Код:

mdb.onPress = playSong("MyDyingBride");
depmode.onPress = playSong("DepecheMode");
intro.onPress = playSong("Intro");

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

chingachgoog 30.10.2008 15:57

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

Код:

var per="внешняя"

function A(par){
        var per="внутренняя А"
        function B(){
                this.per
                var per="внутренняя В"
                trace ("B:\r"+per+"\r"+this.per)
        }
        trace ("А:\r"+per+"\r"+this.per)
        B()
        C()
}

A()

function C(){
        this.per
        var per="внутренняя C"
        trace ("C:\r"+per+"\r"+this.per)
}

add:

Wolsh, у вас выходит аналог делегата, а это типа "не считается" :)
Код:

mc1.onPress=f_set("1")
mc2.onPress=f_set("2")

function f_set(par){
        function setPar(){
                trace(par)
        }
        return setPar
}


iNils 30.10.2008 16:19

Данный код годится только для понимания, как это работает. А практической ценности в нем ноль. Там не только отсутствие фантазии в названии переменных (вспоминается D12 со своими z zz zzz zzzzzzz), но и потенциальная путаница в самих переменных.

laurisgutmanis 30.10.2008 16:39

Смысл инициализировать каждый раз заново функцию - непонятен.
Объяснить довольно сложно, почему так нельзя(не следует, не правильно и т.д.).

И простит меня iNils: но как объяснить человеку, что казарму следует мыть половой щёткой, а не зубной. Это довольно сложно. И доказать тоже.

Как минимум при применении методов "инициализация функции в функции" получается избыточная работа программы. При больших и серьёзных проектах это не допустимо.

А если боишься забыть или потеряться:
1) Называй функцию вменяемо - благо от размера имени функции во Flash ничего не меняется
2) Пиши комментарии

Такие вопросы часто встречаю у своих учеников(правда по PHP).

chingachgoog 30.10.2008 17:55

Цитата:

Сообщение от iNils (Сообщение 773837)
Данный код годится только для понимания, как это работает. А практической ценности в нем ноль. Там ...потенциальная путаница в самих переменных.

iNils, извини, что не оправдал надежд на фантазию с названием переменных :) но код именно для понимания, что путаницы с переменными можно избежать при помощи внутренних функций.
Практическую ценность я не встречал, а специально выдумывать не охота.

iNils 30.10.2008 18:11

Цитата:

Сообщение от chingachgoog (Сообщение 773853)
но код именно для понимания, что путаницы с переменными можно избежать при помощи внутренних функций

Опять театр абсурда.

dashaaa 27.11.2008 16:19

старые песни о главном
 
Подскажите, как в такой ситуации правильно организовать функции, чтобы не было проблем с вложенными функциями и с областями видимости:
Код AS1/AS2:

class Room {
        static var xmlName:String = "1.xml";
        var xmlDoc:XML;
        var xml_rootNode:XMLNode;
 
        public function parsXML():Void {
                trace(xml_rootNode);
        }
        public function Init():Void {
                xmlDoc.load(xmlName);
          // ошибка - обработчик внутри функции.
          // хотя может и нет - обработчик нужен 1 раз и удаляется после использования
          // ?
                xmlDoc.onLoad = function():Void  {
                        if (this.status == 0) {
                                trace("Успешно");
                                xml_rootNode = this.firstChild;
                                // !!!!! ошибка - эта функция "не видна" отсюда
                                parsXML();
                        } else {
                                trace("Ошибка при чтении xml "+this.status);
                        }
                };
        }
        public function Room() {
                xmlDoc = new XML();
        }
}


iNils 27.11.2008 16:26

Код:

import mx.utils.Delegate;
class Room {
        static var xmlName:String = "1.xml";
        var xmlDoc:XML;
        var xml_rootNode:XMLNode;
 
        public function parsXML():Void {
                trace(xml_rootNode);
        }
        public function Init():Void {
                xmlDoc.load(xmlName);
          // ошибка - обработчик внутри функции.
          // хотя может и нет - обработчик нужен 1 раз и удаляется после использования
          // ?
        }
        public function Room() {
                xmlDoc = new XML();
                xmlDoc.onLoad = Delegate.create (this, onLoadXML);
        }
       
        private function onLoadXML (success:Boolean):Void
                //// this теперь ссылается на экземпляр класса Room
                if (success) {
                        trace("Успешно");
                        xml_rootNode = xmlDoc.firstChild;
                                                        // !!!!! ошибка - эта функция "не видна" отсюда
                        parsXML();
                } else {
                        trace("Ошибка при чтении xml "+xmlDoc.status);
                }
        };


}


dashaaa 27.11.2008 17:44

Спасибо большое! Я до этого понятия не имела о Delegate.

faust.d503 27.11.2008 19:35

А зачем использовать Delegate, по-моему есть более простой и удобный способ:

Код AS1/AS2:

class Room {
 
        public function Room() {
                var self:Room = this
                xmlDoc = new XML();
                xmlDoc.onLoad = function(){
                        self.onLoadXML();
                }
        }
 
        private function onLoadXML (success:Boolean):Void
 
        };
 
}


etc 27.11.2008 21:35

faust.d503, это кривой и неудобный способ.

faust.d503 28.11.2008 13:18

__etc
Расскажите, чем он кривой и неудобный и чем лучше Delegate. Нет конечно можно подключить дополнительный класс, чтобы повысить вес программы, но зачем.

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

etc 28.11.2008 13:22

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

faust.d503 28.11.2008 13:32

Цитата:

Такой обработчик нельзя удалить
Почему?
Код AS1/AS2:

class Room {
 
        public function Room() {
                var self:Room = this;
                xmlDoc = new XML();
                xmlDoc.onLoad = function(){
                        self.onLoadXML();
                        //удаляем)
                        delete this.onLoad;
                }
        }
 
        private function onLoadXML (success:Boolean):Void
 
        };
 
}


etc 28.11.2008 13:37

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

iNils 28.11.2008 13:46

О, уже костыли пошли, а было "более простой и удобный способ".


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

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