Oberon space
General Category => Общий раздел => Тема начата: ilovb от Май 11, 2012, 02:54:14 pm
-
Стыдно признаться, но никак не могу понять зачем оно нужно. Как работает понимаю, а хде оно могло бы пригодиться не врубаюсь.
Где можно почитать по теме, чтоб вот сразу стало понятно что без них жить нельзя?
Ну или примерчик какой доходчивый не эзотерический.
Смотрел на руби и на жабаскрипте примеры, но профита не понял.
p.s. Али это все от лукавого?
-
Смотрел на руби и на жабаскрипте примеры, но профита не понял.
Профит очень простой: запаковать состояние (переменные и прочие кишки) в одном месте в передать в другое, чтоб там тупо использовать (понятия не имея о внутренней сложности). Прямой аналог абстрагирования из ООП, но только дешевый и сердитый (без интерфейсов и без объектов) - это просто функция, которую можно вызвать и больше ничего.
-
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?
Ну т.е. мне не понятно нафиг его везде пихают.
-
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?
Ну т.е. мне не понятно нафиг его везде пихают.
Нет. Я ж говорю - это дешево сердито. Но можно и без замыканий, используя классическое ООП. В обероне, например, только так и можно. Но будет очень многословно по сравнению с вариантом, когда замыкания поддержаны в языке.
Например, классическая функция сортировки. На входе элементы и функция сравнения. Теперь представь, что для того, чтобы реализовать функцию сравнения (сравнение двух элементов) тебе недостаточно знать сравниваемые элементы (которые передаются как аргументы), а нужен еще какой-то дополнительный контекст. Как получить доступ к этому контексту из функции? Можно через глобальную переменную, можно добавить ссылку на контекст в сам элемент. Но это очень некрасиво. ООП решение - функция сортировки принимает объект, у которого будет вызываться функция сравнения, в этот объект можно положить ссылку на контекст. В случае замыканий: в месте вызова функции сортировки к функции сравнения "привязывается" нужный контекст (как еще один аргумент или еще как).
-
Непонятно :) Какой там может быть нужен контекст?
У меня мозг наверно как-то иначе пашет. :(
Если не в лом, приведи пример кода с ООП и с замыканиями для сравнения пожалуйста.
-
Непонятно :) Какой там может быть нужен контекст?
У меня мозг наверно как-то иначе пашет. :(
Если не в лом, приведи пример кода с ООП и с замыканиями для сравнения пожалуйста.
Пример на мерзком жабаскрипте:
function compate_using_context(elements, context)
{
function compare(a, b) // наша функция сравнения
{
return context.eval(a) - context.eval(b); // context "виден" здесь, т.е. "привязан" к compare()
}
sort(elements, compare); // библиотечная функция sort(), ничего не знает про context
}
Попробуй подумать и написать тоже самое на обероне ;)
-
Любопытно. А контекст в данном случае это что? Объект ведающий о типах a, b?
Разве сама коллекция не имеет метода compare?
-
Я дико извиняюсь конечно за назойливость.
Просто дюже хочется понять, а сколько попыток не предпринимал все никак.
-
Я вот приведу пару примеров (что сразу нагуглились)
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
-
Любопытно. А контекст в данном случае это что? Объект ведающий о типах a, b?
Да, например, он знает как их сравнивать.
Разве сама коллекция не имеет метода compare?
Если речь идет об обобщенной коллекции, в которую можно положить любые элементы , то она не может иметь такой метод - она не может знать как их сравнивать. Можно, конечно, каждый раз наследовать коллекцию и перегружать этот метод - но это извращение на тему "как неправильно использовать ООП".
-
Хм... Тогда мне не понятны две вещи:
1. Если коллекция однородная, то ее в любом случае нужно наследовать и уточнять, и естественно нужно перегружать метод.
2. Для context этот метод ведь тоже нужно написать. А какая разница где я его напишу?
-
Хотя вот тут в принципе понятно:
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; }"
И кстати в Руби профит вроде в том же заключается. Но при чем тут замыкания?
-
Конкретно профит в том, что можно так написать "{ return elem * x; }"
Нет, самый большой профит в том, что можно сослаться на локальную переменную x (тот самый контекст). Если бы у тебя не было замыкания - тебе пришлось что-то придумывать, чтобы использовать x в отдельной неанонимной функции.
-
Хм... Тогда мне не понятны две вещи:
1. Если коллекция однородная, то ее в любом случае нужно наследовать и уточнять, и естественно нужно перегружать метод.
Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.
2. Для context этот метод ведь тоже нужно написать. А какая разница где я его напишу?
Есть разница между "просто написать метод" и "наследовать коллекцию и перегружать метод каждый раз, когда нужно по-своему отсортировать элементы"? Не говоря о том, что коллекция может быть уже "спущена сверху" и наследовать нет никакой возможности, надо просто правильно отсортировать.
-
Нет, самый большой профит в том, что можно сослаться на локальную переменную x (тот самый контекст). Если бы у тебя не было замыкания - тебе пришлось что-то придумывать, чтобы использовать x в отдельной неанонимной функции.
Ну в паскалях например так не напишешь. Это ведь не фича замыкания, а фича самого языка.
Я про то, что в данном примере этот х явно в месте вызова записан.
Ну то есть фича лишь опирается на замыкания. Или, иначе говоря, фича невозможна без замыканий.
Но профит на мой взгляд тут именно в этой фиче, а не в замыкании.
Если в языке нет такой формы записи анонимных делегатов, то и профита из додиезовского примера не будет.
-
Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.
Т.е. коллекция разнородная? Разнородная в том смысле, что их нельзя сравнить по базе (Object)?
Тогда что за чудо метод у объекта context, который умеет яблоки с холодильниками сравнивать?
Или тут еще поддержка обобщенки нужна?
Есть разница между "просто написать метод" и "наследовать коллекцию и перегружать метод каждый раз, когда нужно по-своему отсортировать элементы"? Не говоря о том, что коллекция может быть уже "спущена сверху" и наследовать нет никакой возможности, надо просто правильно отсортировать.
Тут в принципе согласен. С перегрузкой трудоемко будет...
-
Ну в паскалях например так не напишешь.
Об чем и речь ;) В паскалях нет замыканий. В дельфях есть очень ограниченная поддержка - там "замыкается" указатель на объект так, что в рассмотренную функцию sort можно отдавать метод объекта (а в объекте уже может лежать нужный контекст).
Это ведь не фича замыкания, а фича самого языка.
Э... Вот это самая фича языка и называется "поддержка замыканий". Создатели языка могут обозвать, углубить и расширить ее как угодно.
Я про то, что в данном примере этот х явно в месте вызова записан.
Ну да. Но при этом 'x' скрыт от реализации ConvertAll. PROFIT! :)
-
Э... зачем? Все элементы - наследники какого-нибудь Object и вперед.
Т.е. коллекция разнородная? Разнородная в том смысле, что их нельзя сравнить по базе (Object)?
Она может быть однородная. Все яблоки. Все наследники Object.
Тогда что за чудо метод у объекта context, который умеет яблоки с холодильниками сравнивать?
Или тут еще поддержка обобщенки нужна?
Этот метод знает, что там лежат яблоки, знает как кастнуть из Object в яблоко, и знает, как сравнивать яблоки. А вот коллекция вообще ничего не знает, даже про яблоки, не говоря об их сравнении. Обобщенку пока оставим в стороне, она к замыканиям ортогональна.
-
Об чем и речь В паскалях нет замыканий. В дельфях есть очень ограниченная поддержка - там "замыкается" указатель на объект так, что в рассмотренную функцию sort можно отдавать метод объекта (а в объекте уже может лежать нужный контекст).
...Вот это самая фича языка и называется "поддержка замыканий". Создатели языка могут обозвать, углубить и расширить ее как угодно.
Уфф. Все, теперь я понял. :)
Я то на классическое определение опирался:
Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.
И пытался понять смысл именно как если бы разрешить в паскале возвращать вложенную функцию с сохранением контекста. А с такой формой записи как в додиезе, ясен перец удобно. :)
-
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
Жирным выделил цену вопроса.
Т.е. додиезовский пример отличается только формой записи.
-
И мне еще одна вещь в замыканиях осталась непонятной:
Вот в примере на КП например все понятно с памятью. А как замыкания память выделяют/освобождают совершенно не ясно.
А вот этот вариант в плане освобождения памяти для меня вообще загадка:
function counter()
{
var num = 0;
return function()
{
return ++num;
}
}
var c = counter();
c(); //1
c(); //2
c(); //3
Их GC собирает потом?
-
В дельфях есть очень ограниченная поддержка - там "замыкается" указатель на объект так, что в рассмотренную функцию sort можно отдавать метод объекта (а в объекте уже может лежать нужный контекст)
Это типа как делегаты в ActiveOberon?
-
И мне еще одна вещь в замыканиях осталась непонятной:
Вот в примере на КП например все понятно с памятью. А как замыкания память выделяют/освобождают совершенно не ясно.
GC все делает. В принципе можно и без GC (в С++ есть реализация замыканий), но уже заморочней.
-
Переписал этот пример на КП:
Правда тут будут некоторые накладные расходы на вызов метода. С делегатами было бы совсем гуд.
-
Правда тут будут некоторые накладные расходы на вызов метода. С делегатами было бы совсем гуд.
Если речь о дотнетных делегатах, то они тоже громоздкие и тормозные. Гуд был бы с простым указателем на статическую процедуру + указатель на this-объект.
-
Гуд был бы с простым указателем на статическую процедуру + указатель на this-объект.
Я вот такие и имел ввиду. В ActveOberon делегаты вроде так и сделаны.
-
Так и думал, что эффективно это фиг сделаешь:
Фунарги (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)
-
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?
Ну т.е. мне не понятно нафиг его везде пихают.
Самый ТРУъ-ООП язык -- это смоллток, а там все эти циклы и ветвления как раз на замыканиях сделаны...
-
Я правильно понимаю, что если язык с ООП, то замыкания не нужны?
Ну т.е. мне не понятно нафиг его везде пихают.
Самый ТРУъ-ООП язык -- это смоллток, а там все эти циклы и ветвления как раз на замыканиях сделаны...
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
-
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
-
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
Кот в студию! Как там без вшитого в язык if'a обойтись?
-
Погоди. Как цикл через замыкание - понимаю, но ветвление (if) то как?
Точно так же, только в случае с ветвлением будет не одно замыкание, а два (then-ветка и else-ветка)...
Кот в студию! Как там без вшитого в язык if'a обойтись?
Ну там суть простая -- "оператор" IF является по сути просто объектом, обрабатывающим сообщения, посланные ему, и если эти сообщения помечены тегами "THEN" или "ELSE", то он их выполняет в зависимости от значения условия. А вот эти "THEN" или "ELSE" как раз и являются замыканиями -- анонимными функциями, в которые засунут контекст программы...