Форум: "Прочее";
Текущий архив: 2009.08.09;
Скачать: [xml.tar.bz2];
ВнизНе могу понять замыкания Найти похожие ветки
← →
тимохов © (2009-06-08 01:18) [0]Товарищи и соратники!
Пожалуйста, расскажите о замыканиях и о том, что они могут дать в Дельфи. Я к теме замыканий подступаюсь не первый год. Не сказать, чтобы сильно копаю, но просто хочу понять - что за парадигма такая. Но каждый раз натыкаюсь на выдуманность примеров и невнятность повествования.
Пожалуйста, прошу не приводить примеры для функциональных и других языков (например, для JavaScript), т.к. Дельфи - полноценный ООП-язык, которому не требуются подпорки замыканий в части реализации инкапсуляции.
Заранее спасибо. Особенно буду благодарен за хорошие статьи по теме.
← →
тимохов © (2009-06-08 01:20) [1]Собсно вопрос возник после чтения статьи http://habrahabr.ru/blogs/webdev/38642/
Не сказать, чтобы я не понял что-то. Но мне видится, что использование замыканий в статье - пример недостатков Прототипо-ориентированности JavaScript как такового, а не достоинства замыканий.
← →
Игорь Шевченко © (2009-06-08 01:43) [2]В Pascal-е издревне существуют вложенные функции, которые могут ссылаться на переменные/параметры внешней функции. В других языках не так (в С, например, их нет).
← →
тимохов © (2009-06-08 01:47) [3]2Игорь.
Все бы так, но эта сволочь (ну которая замыкание) контекст еще запоминает! Это все равно, что вложенная функция могла бы жить без внешней, пользуясь ее локальными переменными. Для меня вообще бред. Я не понимаю зачем все это, кроме как навредить создаваемому приложению.
← →
antonn © (2009-06-08 02:03) [4]оффтоп :)
напомнило "...но шмель об этом не знает и летает только поэтому" :)
замыкания какие то... нет чтобы попроще - вложенные функции :)
← →
тимохов © (2009-06-08 02:08) [5]
> замыкания какие то... нет чтобы попроще - вложенные функции
> :)
в том то и дело, что это не вложенные функции не фига )
это что-то большее, чем нам с тобой (если будем писать на дельфи) придется пользоваться.
← →
sniknik © (2009-06-08 02:17) [6]> Это все равно, что вложенная функция могла бы жить без внешней, пользуясь ее локальными переменными.
в javascript так и есть...
например делаешь функцию, в ней определяешь другую которая "вешается" на какое нибудь событие, и в этой внутренней функции используешь переменные определенные выше, т.е. в основной.
и что получается тогда, чтобы "повесить" событие, вызываешь основную, все она более не работает, но при каждом событии отрабатывает внутренняя, и в ней используется "запомненный контекст" т.е. внешние по отношению к ней переменные.
зачем нужно? ну например один раз описав процедуру которая вешает событие на какой нибудь "div" я ее могу вызвать для нескольких, и с одной стороны не боятся, что переменные пересекутся, с другой уверен что используются локальные (глобальные немного "тормознее").
> Для меня вообще бред. Я не понимаю зачем все это, кроме как навредить создаваемому приложению.
просто нельзя рассматривать "фичу" одного языка в контексте другого.
← →
Игорь Шевченко © (2009-06-08 02:21) [7]Контекст может располагаться не только на стеке...
← →
Alex Konshin © (2009-06-08 04:36) [8]
> Игорь Шевченко © (08.06.09 01:43) [2]
> В Pascal-е издревне существуют вложенные функции, которые
> могут ссылаться на переменные/параметры внешней функции.
> В других языках не так (в С, например, их нет).
Игорь, ты прав, что их нет в C, но в GCC C++ они есть, хотя и немногие об этом знают. И вообще в GCC много чего есть, но нет нормальной документации.
По поводу запоминания контекста. В Java это тоже так в аналогичной ситуации. Только там нет вложенных функций и даже нельзя передать указательна функцию. Приходится создавать объект с методом. Так вот, значения из текущего контекста, используемые методом, неявно сохраняются как константы в новом объекте. Думаю, что в JavaScript точно такая же технология используется.
← →
Alex Konshin © (2009-06-08 04:38) [9]По поводу расширений GCC http://linux.yaroslavl.ru/docs/prog/gcc/gcc1-4.html
Наверняка можно найти и первоисточник, но мне лень было искать и я привёл первую попавшуюся ссылку на русском языке.
← →
Игорь Шевченко © (2009-06-08 10:12) [10]Alex Konshin © (08.06.09 04:36) [8]
Я первый раз слово __closure увидел в C++ Builder, там этим модификатором обозначались обработчики событий.
← →
oxffff © (2009-06-08 10:17) [11]Дмитрий, приветствую.
Внимательно прочитай.
Замечательные примеры.
Is it a llama? Is it a lambada? No, it"s the lambda! - by Jarle Stabell
http://edn.embarcadero.com/article/33336
← →
Тимохов_ (2009-06-08 11:05) [12]
> Alex Konshin © (08.06.09 04:36) [8]
>
> По поводу запоминания контекста. В Java это тоже так в аналогичной
> ситуации. Только там нет вложенных функций и даже нельзя
> передать указательна функцию. Приходится создавать объект
> с методом. Так вот, значения из текущего контекста, используемые
> методом, неявно сохраняются как константы в новом объекте.
> Думаю, что в JavaScript точно такая же технология используется.
Спасибо, Alex. Я аналогию понял. Не понял сути. :(
Когда я интересовался Java (было такое), то видел, что нет там ссылок на функции. Все аналоги ивентов делаются реализацией в каком-то классе какого-то интерфейса - передавая объект, по сути передаешь как бы ссылку на методы (ивенты), при этом еще и передаешь контекст (сам объект). Причем сам объект (читай, контекст) никуда не пропадает - ты всегда можешь к нему обратиться и получить, например, результаты работы методов (читай, ивентов).
Тут мне все ясно!
Другой вопрос, контекст, сохраненный в рамках outer-функции. Тут я вообще не понимаю не фига. Например, такой псевдо-код:proc Init();
var
I;
begin
I := 0;
func Inner(A);
begin
I := I + A;
end;
RegisterCallback(Inner);
end;
var
Global_Callback: TProc;
proc RegisterCallback(Callback: TProc);
begin
Global_Callback := Callback;
end;
proc MainAlgorith();
begin
что-то делается, результат передается в Global_Callback;
end;
begin
Init();
MainAlgorith();
Вопрос - как достать результат работы
MainAlgorith(), сохраненный в локальной переменной
функции Init();
end;
Вопрос в коде - как получить I из Init?
В твоем примере про объекты, методы и контексты - я бы просто обратился к члену объекта. А тут как? Где добыть I?
> oxffff © (08.06.09 10:17) [11]
Спасибо, вечером почитаю.
← →
Alkid © (2009-06-08 11:15) [13]
> Тимохов_ (08.06.09 11:05) [12]
ЕМНИП, в Java есть inner-классы, которые содержат неявную ссылку на экземпляр внешнего класса, в чьём контексте были созданы, но не являются подлинными лексическими замыканиями, т.е. локальные переменные контекста, в котором были созданы, они не захватывают.
← →
jack128_ (2009-06-08 13:08) [14]Дим, попробуй Linq to objects в шарпе. C# тоже полноценный(возможно более полноценный, чем дельфи) и ничего, не чурается замыканий. Собственно в том виде, в котором анонимные методы в дельфе сделаны - они применимы только в ограниченных случаях. Тот же Linq for objects на них не построишь. Точнее в принципе сделать можно - но использовать это будет ОЧЕНЬ неудобно.
← →
Тимохов_ (2009-06-08 13:47) [15]Жень. Возьми на себя евангелистсткую миссию - расскажи про замыкания и Linq! :) Объясни своими словами что-ли.
Я, например, уже C# не пользуюсь. Даже не стоит. Поэтому посмотреть не могу.
← →
sniknik © (2009-06-08 13:55) [16]> В твоем примере про объекты, методы и контексты - я бы просто обратился к члену объекта.
а в javascript все объекты, строки, числа, функции, все. т.е. например вызывая функцию fn() ты на самом деле вызываешь метод объекта - obj.fn(), у которого само собой есть ссылка на родительский. а те переменные, которые кроме того само собой тоже объекты, еще и его проперти.
т.е. если хочешь сделать также, то должен повторить модель, потому наверное и в его примере "про объекты, методы и контексты".
> А тут как? Где добыть I?
а никак, в паскале эта переменная определяется в стеке, выполнилась функция - все, забудь про нее.
← →
Игорь Шевченко © (2009-06-08 13:58) [17]
> а никак, в паскале эта переменная определяется в стеке,
> выполнилась функция - все, забудь про нее.
локальную переменную необязательно в стеке объявлять. Например const она вовсе не в стеке.{$WRITEABLECONST ON}
procedure TForm1.Button1Click(Sender: TObject);
const
NCalls: Integer = 0;
begin
Inc(NCalls);
ShowMessageFmt ("you clicked me %d times", [NCalls]);
end;
← →
Mystic © (2009-06-08 14:10) [18]Замыкания вообще вещь более специфичная для функциональных языков. Там обычная ситуация, когда функция передается в качестве параметра в другую функцию. Но для того, чтобы этим пользоваться было удобно, надо иметь легкий механизм конструирования новых функций. Замыкание это один из таких способов.
Например, есть некоторое событие. И надо передать в это событие дополнительный параметр. Это легко сделать при помощи замыкания.
В общем, функциональное программирование обычно требует несколько иного подхода, чем обычное.
← →
sniknik © (2009-06-08 14:18) [19]> локальную переменную необязательно в стеке объявлять. Например const она вовсе не в стеке.
потому что она глобальная, просто объявлена локально (предположительно нигде больше не используется, но верни ее адрес из функции и пользуйся вне ее... если есть желание делать через задницу)
← →
Тимохов_ (2009-06-08 14:45) [20]
> sniknik © (08.06.09 13:55) [16]
Я понимаю, что JS сильно другой язык, нежели дельфи. Насколько я понимаю, он прототипо-ориентированный, а не объекто-ориентированный. Т.е. я как бы понимаю, что замыкания там могут играть роль инкапсуляторов - т.е. inner функция получает доступ к контексту, как бы к private переменным - имитируется инкапсуляция.
Ты можешь в дельфи привести пример?
Вот хоть убей меня, я не понимаю чем отличается замыкание от локальных функций? Ведь отличается чем-то. Но не пойму чем. Можешь привести хотя бы 2 отличительных признака?
Заранее благодарю.
← →
jack128_ (2009-06-08 14:59) [21]
> Жень. Возьми на себя евангелистсткую миссию - расскажи про
> замыкания и Linq! :)
Косноязычен я к сожалению.
Можешь вот почитать: http://www.rsdn.ru/article/dotnet/LinqAsStapToFp.xml Простым доступным языком написано. там кой какие заморочки шарпа описаны, ну судя по d2009 код жир тем же путем пойдет. Видимо хочет о каждую граблю лично лбом стукнуться, место того, чтоб учесть опыт MS C#
← →
sniknik © (2009-06-08 15:09) [22]> Ты можешь в дельфи привести пример?
могу попробовать. только зачем? неуклюже получится.
> чем отличается замыкание от локальных функций?
вот тебе аналогия
локальная(?) вернее просто функция в javascript это объект, с методом. чтобы использовать этот объект в функциональном прошу заметить языке, определяется функция с набором переменных, и возвращаемым значением - другая функция, вложенная, у которой есть доступ к этим переменным.
вызвав первую функцию, ты создаешь объект, но сохраняешь ссылку не на него а на возвращаемую внутреннюю функцию у которой эта ссылка определена по умолчанию. и любой последующий вызов этой "вернутой" функции использует созданный для нее объект внешней.
по моему все просто.
хочешь еще объект, еще раз вызываешь(создаешь) внешнюю и запоминаешь возвращаемую функцию для работы с переменными этого объекта.
т.е. то же самое объектное программирование, только "вывернутое наизнанку". доступ "изнутри", а не "сверху" объекта.
← →
Mystic © (2009-06-08 15:11) [23]> Ты можешь в дельфи привести пример?
Я в Delphi про замыкания не знаю, если замыкания из JavaScript, то... Например, TList.Sort. В этот метод надо передать функцию Compare. Эта функция принимает два указателя. Но может быть, что сортировка списка зависит от того, какие галочки пользователь выбрал на форме, т. е. зависеть от каких-то параметров a, b, c. При этом если комбинаторный взрыв мешает написать отдельно функцию на каждый возможный вариант сортировки. Получается примерно следующее:
procedure DoSort(List: TList; UkraineCompare: Boolean; CaseInsensitive: Boolean);
function Compare(Item1, Item2: Pointer): Integer;
begin
if UkraineCompare then
if CaseInsensitive then
...
Result := 0;
end;
begin
List.Sort(@Compare);
end;
← →
Тимохов_ (2009-06-08 15:13) [24]
> sniknik © (08.06.09 15:09) [22]
> "вывернутое наизнанку"
Вот ключ моего непонимания! Надо смотреть на все это с изнанки :)
Спасибо все, сегодня вечером посижу, почитаю и подумаю, т.к. разобраться с этими замыканиями все же хочу хорошо, а то чую себя каким-то ретроградом :)
Что не пойму, то спрошу здесь.
← →
jack128_ (2009-06-08 15:17) [25]Вот кста код, использующий способность захватывать контекст внешней функции даже после её(функции) завершения.
Можешь попробывать реализовать TMyComponent в традиционном стиле. Сравни сколько кода у тебя получится.program Project5;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
// Фактически - эта функция - это и есть енумератор.
// должна вернуть false - если больше нет элементов,
// иначе - true и сам элемент в параметре NextItem
TEnumerateFunc<T> = reference to function (var NextItem: T): boolean;
TEnumerator<T> = record
strict private
FFunc: TEnumerateFunc<T>;
FCurrent: T;
public
constructor Create(const AEnumerateFunc: TEnumerateFunc<T>);
property Current: T read FCurrent;
function MoveNext: Boolean;
end;
TEnumeratorFactory<T> = record
strict private
FFunc: TEnumerateFunc<T>;
public
constructor Create(const AEnumerateFunc: TEnumerateFunc<T>);
function GetEnumerator: TEnumerator<T>;
end;
constructor TEnumeratorFactory<T>.Create(const AEnumerateFunc: TEnumerateFunc<T>);
begin
FFunc := AEnumerateFunc;
end;
function TEnumeratorFactory<T>.GetEnumerator: TEnumerator<T>;
begin
Result := TEnumerator<T>.Create(FFunc)
end;
constructor TEnumerator<T>.Create(const AEnumerateFunc: TEnumerateFunc<T>);
begin
FFunc := AEnumerateFunc;
end;
function TEnumerator<T>.MoveNext: Boolean;
begin
Result := FFunc(FCurrent)
end;
// Теперь пример.
// допустим мы хотим чтобы в зависимости от параметра получать вложенные компоненты в прямой или же обратной последовательности:
type
TMyComponent = class(TComponent)
public
function GetComponents(AForward: boolean): TEnumeratorFactory<TComponent>;
end;
function TMyComponent.GetComponents(AForward: boolean): TEnumeratorFactory<TComponent>;
var
Index: Integer;
Func: TEnumerateFunc<TComponent>;
begin
if AForward then
begin
Index := -1;
Func := function (var NextItem: TComponent): boolean
begin
Index := Index + 1; // переманная Index определена вне анонимного метода.
Result := Index < (ComponentCount - 1);
if Result then
NextItem := Components[Index]
end;
end
else begin
Index := ComponentCount;
Func := function (var NextItem: TComponent): boolean
begin
Index := Index - 1;
Result := Index >= 0;
if Result then
NextItem := Components[Index]
end;
end;
Result := TEnumeratorFactory<TComponent>.Create(Func);
end;
ну и пример а теперь от мечтаний - к реалием. Все это не работает, потому что delphi2009 - глюкав как не знаю что.
var
MyComp: TMyComponent;
C: TComponent;
begin
try
MyComp := TMyComponent.Create(nil);
try
for C in MyComp.GetComponents(True) do; // ERROR!!!!!!!!!!!!!!!!!!! [DCC Error] Project5.dpr(89): E2010 Incompatible types: "TEnumerator<Classes.TComponent>" and "Pointer
finally
MyComp.Free
end;
except
on E:Exception do
Writeln(E.Classname, ": ", E.Message);
end;
ReadLn;
end.
← →
jack128_ (2009-06-08 15:21) [26]Кста, Дим, оффтоп небольшой. Что такое "Private Report" Баг баг репорт по этому коду стал приватным. Насколько я понимаю его терь никто кроме меня неможет видить. Не знаешь что это за хрень??
← →
sniknik © (2009-06-08 15:30) [27]пример из ссылки
function createCounter() {
var numberOfCalls = 0;
return function() {
return ++numberOfCalls;
}
}
var fn = createCounter();
fn(); //1
fn(); //2
fn(); //3
на дельфи (ну, более менее, например т.к. в D7 лямда функций нет то пришлось обьявить. ну и не стал возвращать значение просто показываю)unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
Tinner = procedure of object;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
inout: Tinner;
end;
TCounter = class
Count: integer;
function Init: Tinner;
procedure Inner;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TCounter.Init: Tinner;
begin
Count:= 0;
result:= Inner;
end;
procedure TCounter.Inner;
begin
Inc(Count);
ShowMessage(IntToStr(Count))
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
inout:= TCounter.Create.Init();
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
inout;
end;
end.
← →
Тимохов_ (2009-06-08 15:31) [28]
> jack128_ (08.06.09 15:17) [25]
Ну что сказать по твоему примеру - понять и идейно красиво, ничего не скажешь. Т.е. используя замыкание ты избегаешь того, что тебе нужно будет делать 2 класса, которые будут реализовывать итераторы - один для возрастания, другой для убывания. Да, ловко.
Интересно, конечно, как они в компиляторе реализовали управление памятью :)
Будет время поставлю Д2009 и посмотрю.
> jack128_ (08.06.09 15:21) [26]
Это значит, что ты попал - за тобой уже выехали!
Если серьезно, то не знаю - спроси в группе соответствующей (если репорт по компилятору, то спроси в компиляторной группе - там должен быть ответственный).
← →
jack128_ (2009-06-08 15:36) [29]
> Интересно, конечно, как они в компиляторе реализовали управление
> памятью :)
Тревиально.
Вот это
type
TIntFunc = reference to function: Integer;
эквивалентно:
type
IntFunc = interface
function Invoke: Integer;
end;
каждая конкретная функция - преобразуется в класс, который реализует этот интерфейс.
← →
Sergey Masloff (2009-06-08 15:52) [30]jack128_ (08.06.09 15:17) [25]
О, наконец-то старому ретрограду мне стало понятно зачем хоть одна из новых фич нужна с практической точки зрения. Пример отличный, спасибо.
← →
Игорь Шевченко © (2009-06-08 16:38) [31]jack128_ (08.06.09 15:17) [25]
Был язык, как язык, а стал С++ какой-то. Мерзость
← →
Тимохов_ (2009-06-08 16:51) [32]
> Игорь Шевченко © (08.06.09 16:38) [31]
>
> jack128_ (08.06.09 15:17) [25]
>
> Был язык, как язык, а стал С++ какой-то. Мерзость
В полку ворчунов прибыло: Игорь, теперь я с тобой!
:)
Маркетинг, гребаный - если лямбда-исчисление нынче модно, то нужно его и в Дельфи засунуть - авось будет что красивое показать на презентации. :(
← →
Игорь Шевченко © (2009-06-08 17:10) [33]Тимохов_ (08.06.09 16:51) [32]
Видишь ли, Дима, все хорошо в меру. Может быть, и templates хороши, но уже не для меня - после стольких лет на паскале я привык к вполне определенным конструкциям языка.
Может, оно для тех, кто сегодня увидел впервые паскаль в виде D2009 и станет привычным, а мне так глаз режет.
← →
Тимохов_ (2009-06-08 17:27) [34]
> Игорь Шевченко © (08.06.09 17:10) [33]
>
> Тимохов_ (08.06.09 16:51) [32]
>
> Видишь ли, Дима, все хорошо в меру.
Ты меня, видимо, Игорь, не понял. Я нисколько не нападаю - мне тоже режет.
Я даже так скажу - меня раздражают ситуации, когда одно и то же можно сделать несколькими СИЛЬНО разными СИНТАКСИЧЕСКИМИ способами. Обязательно в команде будет, кто будет любить одну конструкцию, другой другую - и будет бардак: знакомые всем циклы будут реализоваться настолько через задницу, что надо становиться реальным гуру в конкретном синтаксисе, если хочешь пробежаться от первого до последнего символа. Мода - это зло для языков. И не ИМХО. Я до сих пор вычищаю тот синтаксический понос, который сам же и привнес с началом использования Д2006.
Вот Всеволод (это автор второго отчета о семинаре Кодгира 2 июня) спросил у Девида Интерсимоны - а знает ли тот пример какого-то идеального кода, на который все должны равняться. Хороший вопрос, если вдуматься. Они напридумывали кучу всего, а не догадались нанять евангелиста, чтобы тот написал:
а) доступную (не по цене, а физически);
б) хорошую
книгу о том, как надо писать сейчас в дельфи.
Если не читали отчет Всеволода, то ответ был такой - Дельфи есть инструмент, и дело организации разрабатывать свои стандарты его использования. Я уверен порядочный разработчик компиляторов должен следовать фразе - мы в ответе за тех кого приручили. Если ты придумал фичу и показываешь ее в качестве маркетингового преимущества, то ты просто обязан в деталях показать как этим зафигато пользоваться.
Вот по Java была хорошая книга - философия java. Почему такой нет по Delphi?
ЗЫ Прошу ветку не закрывать. Оффтоп прекращаю.
← →
Игорь Шевченко © (2009-06-08 17:32) [35]Тимохов_ (08.06.09 17:27) [34]
Да этот вовсе не оффтоп.
По Delphi примеры, как пользоваться, довольно неплохие, приводят Тейксейра с Пачеко, может, и Кэнту тоже.
← →
Тимохов_ (2009-06-08 17:38) [36]
> По Delphi примеры, как пользоваться, довольно неплохие,
> приводят Тейксейра с Пачеко, может, и Кэнту тоже.
У Тейкстеры есть про замыкания? Или ты имеешь в виду старый, добрый дельфи <= version 7?
У Кенту может быть и есть по новому дельфи. Не может быть а точно есть.
Он на каждую версию дельфи выпускает свой Hand Book. Почему его только не переводят на русский? Хорошая евангелистская книга было бы.
← →
Alkid © (2009-06-08 17:46) [37]
> Тимохов_ (08.06.09 16:51) [32]
> Маркетинг, гребаный - если лямбда-исчисление нынче модно,
> то нужно его и в Дельфи засунуть - авось будет что красивое
> показать на презентации. :(
Замечу в сторону, что к лямбда счислению лямбды в C# и Дельфи почти никак не относятся :) Лямбда-счисление - это Haskell, Curry, Clean и т.п. :)
А вообще, как сказал кто-то, по мере развития все языки становятся всё больше и больше похожи на Лисп :)
← →
Игорь Шевченко © (2009-06-08 17:57) [38]Тимохов_ (08.06.09 17:38) [36]
> У Тейкстеры есть про замыкания? Или ты имеешь в виду старый,
> добрый дельфи <= version 7?
Старый добрый, разумеется.
> Почему его только не переводят на русский?
А почему Borland/Codegear/Embarcadero help не переводят на русский ?
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2009.08.09;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.005 c