Oberon space

General Category => Общий раздел => Тема начата: ilovb от Май 11, 2012, 02:54:14 pm

Название: Замыкания
Отправлено: ilovb от Май 11, 2012, 02:54:14 pm
Стыдно признаться, но никак не могу понять зачем оно нужно. Как работает понимаю, а хде оно могло бы пригодиться не врубаюсь.

Где можно почитать по теме, чтоб вот сразу стало понятно что без них жить нельзя?

Ну или примерчик какой доходчивый не эзотерический.

Смотрел на руби и на жабаскрипте примеры, но профита не понял.

p.s. Али это все от лукавого?
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 03:03:33 pm
Смотрел на руби и на жабаскрипте примеры, но профита не понял.

Профит очень простой: запаковать состояние (переменные и прочие кишки) в одном месте в передать в другое, чтоб там тупо использовать (понятия не имея о внутренней сложности). Прямой аналог абстрагирования из ООП, но только дешевый и сердитый (без интерфейсов и без объектов) - это просто функция, которую можно вызвать и больше ничего.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 03:06:32 pm
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?

Ну т.е. мне не понятно нафиг его везде пихают.
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 03:19:53 pm
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?

Ну т.е. мне не понятно нафиг его везде пихают.

Нет. Я ж говорю - это дешево  сердито. Но можно и без замыканий, используя классическое ООП. В обероне, например, только так и можно. Но будет очень многословно по сравнению с вариантом, когда замыкания поддержаны в языке.

Например, классическая функция сортировки. На входе элементы и функция сравнения. Теперь представь, что для того, чтобы реализовать функцию сравнения (сравнение двух элементов) тебе недостаточно знать сравниваемые элементы (которые передаются как аргументы), а нужен еще какой-то дополнительный контекст. Как получить доступ к этому контексту из функции? Можно через глобальную переменную, можно добавить ссылку на контекст в сам элемент. Но это очень некрасиво. ООП решение - функция сортировки принимает объект, у которого будет вызываться функция сравнения, в этот объект можно положить ссылку на контекст. В случае замыканий: в месте вызова функции сортировки к функции сравнения "привязывается" нужный контекст (как еще один аргумент или еще как).
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 03:31:24 pm
Непонятно  :) Какой там может быть нужен контекст?
У меня мозг наверно как-то иначе пашет.  :(

Если не в лом, приведи пример кода с ООП и с замыканиями для сравнения пожалуйста.
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 03:34:36 pm
Непонятно  :) Какой там может быть нужен контекст?
У меня мозг наверно как-то иначе пашет.  :(

Если не в лом, приведи пример кода с ООП и с замыканиями для сравнения пожалуйста.

Пример на мерзком жабаскрипте:
function compate_using_context(elements, context)
{
    function compare(a, b) // наша функция сравнения
    {
        return context.eval(a) - context.eval(b); // context "виден" здесь, т.е. "привязан" к compare()
    }

    sort(elements, compare); // библиотечная функция sort(), ничего не знает про context
}

Попробуй подумать и написать тоже самое на обероне ;)
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 03:46:01 pm
Любопытно. А контекст в данном случае это что? Объект ведающий о типах a, b?

Разве сама коллекция не имеет метода compare?
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 03:52:34 pm
Я дико извиняюсь конечно за назойливость.
Просто дюже хочется понять, а сколько попыток не предпринимал все никак.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 03:59:57 pm
Я вот приведу пару примеров (что сразу нагуглились)
function counter()
{
   var num = 0;
   return function()
   {
      return ++num;
   }
}

var c = counter();

c(); //1
c(); //2
c(); //3

type
  TGenericFunction = reference to function: string;
 
function Factory(const ASomeText: string):TGenericFunction;
begin
  Result := function: string
    begin
      Result := ASomeText;
    end;
end;
 
var
  f1, f2: TGenericFunction;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  f1 := Factory('First');
  f2 := Factory('Second');
 
  Memo1.Lines.Add(f1);
  Memo1.Lines.Add(f2);
end;

Кому как. По мне так примеры абсолютно сферические  ;D
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 04:01:19 pm
Любопытно. А контекст в данном случае это что? Объект ведающий о типах a, b?

Да, например, он знает как их сравнивать.

Разве сама коллекция не имеет метода compare?

Если речь идет об обобщенной коллекции, в которую можно положить любые элементы , то она не может иметь такой метод - она не может знать как их сравнивать. Можно, конечно, каждый раз наследовать коллекцию и перегружать этот метод - но это извращение на тему "как неправильно использовать ООП".
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 04:14:49 pm
Хм... Тогда мне не понятны две вещи:
1. Если коллекция однородная, то ее в любом случае нужно наследовать и уточнять, и естественно нужно перегружать метод.
2. Для context этот метод ведь тоже нужно написать. А какая разница где я его напишу?
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 04:22:08 pm
Хотя вот тут в принципе понятно:
int[] ary = { 1, 2, 3 };
 int x = 2;
 var ary1 = Array.ConvertAll<int, int>(ary, delegate(int elem) { return elem * x; }); // { 2, 4, 6 }
 // or..
 var ary2 = Array.ConvertAll<int, int>(ary, elem => { return elem * x; }); // { 2, 4, 6 }

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

Конкретно профит в том, что можно так написать: "{ return elem * x; }"

И кстати в Руби профит вроде в том же заключается. Но при чем тут замыкания?
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 04:28:46 pm
Конкретно профит в том, что можно так написать "{ return elem * x; }"

Нет, самый большой профит в том, что можно сослаться на локальную переменную x (тот самый контекст). Если бы у тебя не было замыкания - тебе пришлось что-то придумывать, чтобы использовать x в отдельной неанонимной функции.
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 04:31:59 pm
Хм... Тогда мне не понятны две вещи:
1. Если коллекция однородная, то ее в любом случае нужно наследовать и уточнять, и естественно нужно перегружать метод.

Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.

2. Для context этот метод ведь тоже нужно написать. А какая разница где я его напишу?

Есть разница между "просто написать метод" и "наследовать коллекцию и перегружать метод каждый раз, когда нужно по-своему отсортировать элементы"? Не говоря о том, что коллекция может быть уже "спущена сверху" и наследовать нет никакой возможности, надо просто правильно отсортировать.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 04:35:55 pm
Цитата: vlad
Нет, самый большой профит в том, что можно сослаться на локальную переменную x (тот самый контекст). Если бы у тебя не было замыкания - тебе пришлось что-то придумывать, чтобы использовать x в отдельной неанонимной функции.

Ну в паскалях например так не напишешь. Это ведь не фича замыкания, а фича самого языка.

Я про то, что в данном примере этот х явно в месте вызова записан.

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

Если в языке нет такой формы записи анонимных делегатов, то и профита из додиезовского примера не будет.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 04:45:55 pm
Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.
Т.е. коллекция разнородная? Разнородная в том смысле, что их нельзя сравнить по базе (Object)?
Тогда что за чудо метод у объекта context, который умеет яблоки с холодильниками сравнивать?
Или тут еще поддержка обобщенки нужна?

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

Тут в принципе согласен. С перегрузкой трудоемко будет...
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 04:51:43 pm
Ну в паскалях например так не напишешь.

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

Это ведь не фича замыкания, а фича самого языка.

Э... Вот это самая фича языка и называется "поддержка замыканий". Создатели языка могут обозвать, углубить и расширить ее как угодно.

Я про то, что в данном примере этот х явно в месте вызова записан.

Ну да. Но при этом 'x' скрыт от реализации ConvertAll. PROFIT! :)
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 04:56:41 pm
Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.
Т.е. коллекция разнородная? Разнородная в том смысле, что их нельзя сравнить по базе (Object)?

Она может быть однородная. Все яблоки. Все наследники Object.

Тогда что за чудо метод у объекта context, который умеет яблоки с холодильниками сравнивать?
Или тут еще поддержка обобщенки нужна?

Этот метод знает, что там лежат яблоки, знает как кастнуть из Object в яблоко, и знает, как сравнивать яблоки. А вот коллекция вообще ничего не знает, даже про яблоки, не говоря об их сравнении. Обобщенку пока оставим в стороне, она к замыканиям ортогональна.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 05:01:04 pm
Цитата: vlad
Об чем и речь  В паскалях нет замыканий. В дельфях есть очень ограниченная поддержка - там "замыкается" указатель на объект так, что в рассмотренную функцию sort можно отдавать метод объекта (а в объекте уже может лежать нужный контекст).
...Вот это самая фича языка и называется "поддержка замыканий". Создатели языка могут обозвать, углубить и расширить ее как угодно.

Уфф. Все, теперь я понял.  :)

Я то на классическое определение опирался:
Цитировать
Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.

И пытался понять смысл именно как если бы разрешить в паскале возвращать вложенную функцию с сохранением контекста. А с такой формой записи как в додиезе, ясен перец удобно.  :)
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 08:32:11 pm
int[] ary = { 1, 2, 3 };
 int x = 2;
 var ary1 = Array.ConvertAll<int, int>(ary, delegate(int elem) { return elem * x; }); // { 2, 4, 6 }
 // or..
 var ary2 = Array.ConvertAll<int, int>(ary, elem => { return elem * x; }); // { 2, 4, 6 }
Но тут профит мне кажется больше в анонимности делегата, чем собственно в замыкании.
Конкретно профит в том, что можно так написать: "{ return elem * x; }"

Поясню, почему я так сказал  :)

Переписал этот пример на КП:
наша либа:
MODULE MyLib;
IMPORT Log;
TYPE
Op* = EXTENSIBLE RECORD END;
Arr = ARRAY OF INTEGER;

PROCEDURE (VAR o: Op) Calc*(VAR x: INTEGER), NEW, EMPTY;

PROCEDURE ConvertAll*(VAR a: Arr; VAR o: Op);
VAR i: INTEGER;
BEGIN
FOR i := 1 TO LEN(a) - 1 DO
o.Calc(a[i]);
END;
END ConvertAll;

PROCEDURE Print*(IN a: Arr);
VAR i: INTEGER;
BEGIN
FOR i := 1 TO LEN(a) -1 DO
Log.Int(a[i]);
END;
END Print;

BEGIN
END MyLib.

наш код:
Цитировать
MODULE MyTest;
   IMPORT Log, Lib := MyLib;
   TYPE
      Mul = RECORD(Lib.Op) y: INTEGER END;
   
   PROCEDURE (VAR m: Mul) Calc(VAR x: INTEGER);
   BEGIN
      x := x * m.y;
   END Calc;

   
   PROCEDURE Do*;
   VAR
      mul: Mul;
      a: ARRAY 10 OF INTEGER;
      j: INTEGER;
   BEGIN
      
      FOR j := 1 TO 9 DO
         a[j] := j;
      END;
      
      mul.y := 10;
      
      Lib.ConvertAll(a, mul);
      
      Lib.Print(a);
      
   END Do;   
   
BEGIN
END MyTest.Do

Жирным выделил цену вопроса.

Т.е. додиезовский пример отличается только формой записи.
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 08:44:54 pm
И мне еще одна вещь в замыканиях осталась непонятной:
Вот в примере на КП например все понятно с памятью. А как замыкания память выделяют/освобождают совершенно не ясно.

А вот этот вариант в плане освобождения памяти для меня вообще загадка:
function counter()
{
   var num = 0;
   return function()
   {
      return ++num;
   }
}

var c = counter();

c(); //1
c(); //2
c(); //3

Их GC собирает потом?
Название: Re: Замыкания
Отправлено: ilovb от Май 11, 2012, 09:00:36 pm
В дельфях есть очень ограниченная поддержка - там "замыкается" указатель на объект так, что в рассмотренную функцию sort можно отдавать метод объекта (а в объекте уже может лежать нужный контекст)

Это типа как делегаты в ActiveOberon?
Название: Re: Замыкания
Отправлено: vlad от Май 11, 2012, 09:23:15 pm
И мне еще одна вещь в замыканиях осталась непонятной:
Вот в примере на КП например все понятно с памятью. А как замыкания память выделяют/освобождают совершенно не ясно.

GC все делает. В принципе можно и без GC (в С++ есть реализация замыканий), но уже заморочней.
Название: Re: Замыкания
Отправлено: ilovb от Май 12, 2012, 06:40:18 am
Переписал этот пример на КП:

Правда тут будут некоторые накладные расходы на вызов метода. С делегатами было бы совсем гуд.
Название: Re: Замыкания
Отправлено: Губанов Сергей Юрьевич от Май 12, 2012, 08:52:58 am
Правда тут будут некоторые накладные расходы на вызов метода. С делегатами было бы совсем гуд.
Если речь о дотнетных делегатах, то они тоже громоздкие и тормозные. Гуд был бы с простым указателем на статическую процедуру + указатель на this-объект.
Название: Re: Замыкания
Отправлено: ilovb от Май 12, 2012, 09:01:58 am
Гуд был бы с простым указателем на статическую процедуру + указатель на this-объект.
Я вот такие и имел ввиду. В ActveOberon делегаты вроде так и сделаны.
Название: Re: Замыкания
Отправлено: ilovb от Май 12, 2012, 07:32:37 pm
Так и думал, что эффективно это фиг сделаешь:
Фунарги (http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0_%D1%84%D1%83%D0%BD%D0%B0%D1%80%D0%B3%D0%B0)
Название: Re: Замыкания
Отправлено: Geniepro от Май 14, 2012, 08:13:05 am
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?

Ну т.е. мне не понятно нафиг его везде пихают.

Самый ТРУъ-ООП язык -- это смоллток, а там все эти циклы и ветвления как раз на замыканиях сделаны...
Название: Re: Замыкания
Отправлено: valexey от Май 14, 2012, 08:17:54 am
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?

Ну т.е. мне не понятно нафиг его везде пихают.

Самый ТРУъ-ООП язык -- это смоллток, а там все эти циклы и ветвления как раз на замыканиях сделаны...
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Название: Re: Замыкания
Отправлено: Geniepro от Май 14, 2012, 08:26:18 am
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
Название: Re: Замыкания
Отправлено: valexey от Май 14, 2012, 08:32:57 am
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
Кот в студию! Как там без вшитого в язык if'a обойтись?
Название: Re: Замыкания
Отправлено: Geniepro от Май 14, 2012, 12:04:27 pm
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
Кот в студию! Как там без вшитого в язык if'a обойтись?
Ну там суть простая -- "оператор" IF является по сути просто объектом, обрабатывающим сообщения, посланные ему, и если эти сообщения помечены тегами "THEN" или "ELSE", то он их выполняет в зависимости от значения условия. А вот эти "THEN" или "ELSE" как раз и являются замыканиями -- анонимными функциями, в которые засунут контекст программы...