Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2014.01.19;
Скачать: CL | DM;

Вниз

delphi и обобщенное прораммирование   Найти похожие ветки 

 
vlk32   (2011-11-01 15:20) [0]

Хотел бы узнать мнение уважаемого сообщества по такой вот скользкой теме как обобщенное программирование. Сразу скажу, что я понимаю под этим два аспекта:
- структуры данных с обобщенным типом элементов (контейнеры)
- обобщенные алгоритмы над контейнерами

Недавно я предпринял попытку написать свою библиотеку, используя генерики (несмотря на то, что они пока "сырые"). Прошелся по нескольким граблям, посмотрел как сделаны коллекции по умолчанию в Delphi, как тоже самое устроено в STL и C#. И пришел к таким выводам (поправьте меня, если в чем то неправ), о том как можно все устроить.

0. Эталонный подход STL
[container] <--> [iterator] <--> [generic algorithm]

Пример
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
 {
   for ( ; first!=last; ++first ) f(*first);
   return f;
 }

Использование
for_each (myvector.begin(), myvector.end(), myfunction);

Обратите внимание - никакой информации об обобщенном типе в вызове for_each не используется. Возможно это тема для дискуссии, но мое мнение - реализовать подобную функциональность и синтаксис  в таком же примерно виде в delphi даже с использованием генериков и анономных процедур не представляется возможным

Остаются следующие варианты

1.
Контейнеры выполнены индивидуально.
Каждый контейнер максимально эффективно реализует собственную функциональность с учетом особенностей внутренней структуры хранения элементов.
Семантически идентичные функции вполне могут иметь различный синтаксис. Например, добавление элемента в конец контейнера:
для массива procedure Add(item:T);
для списка  procedure AddNode(node:TListNode);
для стека procedure Push(const Value: T); и т.д. и т.п.
Так сделано в Generics.Collections где все контейнеры унаследованы от TEnumerable<T> - беспонтового класса необходимого только для того чтобы с этими контейнерами писать for item in <container> do begin ..do some with item end;
Естественно, что ни о каких обобщенных алгоритмах речи не идет. Т.е. вся экстра-функциональность впихивается либо в базовый контейнер либо в его потомков. И так для каждого контейнера по отдельности.

2.
Контейнеры унаследованы от некоего базового класса (TIterator), который описывает максимально возможную функциональность итератора/курсора, т.е. возможность устанавливать курсор на требуемый элемент контейнера, перемещать курсор, читать/писать в элемент под курсором, вставлять/удалять элементы  т.д. Каждый наследник переопределяет те функции курсора, которые он реально поддерживает, а на остальные вешает генерацию исключений. Кроме того наследник определяет несколько функций которые возвращают готовые курсоры на начало, конец, произвольную позицию в контейнере. Сам курсор может быть записью такого типа

Cursor = record
 flags : <состояние курсора>;
 Owner : TIterator; <<< ссылка на владельца курсора
 case <тип контейнера> of
  Array: ( position : Integer);
  List: (dnode : TListNode);
  < и т.д. >  
end;

Обобщенные алгоритмы получают на вход контейнеры и курсоры для реализации своей функциональности.
Этот подход хорош тем, что в объявлении Cursor не указан никакой обобщенный тип, соответственно все курсоры это один и тот же тип, а вся конкретика реализована через функции владельца курсора. Курсоры могут копироваться и присваиваться. Никаких новых объектов при этом не создается.
Так сделано в мощнейшей для своего времени библиотеке DeCAL.


 
vlk32   (2011-11-01 15:21) [1]

<продолжение>
3.
Использование интерфейсов. Вся функциональность итератора/курсора разбивается на несколько интерфейсов по уровню функциональности (или какому то другому критерию). Все контейнеры которые мы хотим задействовать в обобщенных алгоритмах должны наследовать и реализовать интерфейсы курсора. В данном подходе никаких отдельных курсоров (в виде объектов или записей) не используется. В обобщенные алгоритмы передаются контейнеры и дополнительные параметры необходимые для выполнения конкретной операции (например индекс стартового элемента, индексы начала и конца диапазона и т.п.).
Так сделано в C#. Этот подход кажется мне не совсем удачным в таком плане:
- в обобщенные алгоритмы надо будет передавать индексы элементов вместо курсора. При этом для каких то типов контейнеров лучше чтобы это были именно целочисленные индексы, а для других указатели на элемент контейнера, т.о. на лицо нарушение общности.
- т.к. контейнер объявлен с обобщенным типом, то и обобщенная процедура также должна быть объявлена с обобщенным типом, т.е.
procedure ForEach<T>(C:TContainer<T>; Action:TActionOverSingleItem<T>); что не даст нам реализовать это через глобальную процедуру. Один из выходов сделать так:


Algo<T> = class
class procedure ForEach(C:TContainer<T>; Action:TActionOverSingleItem<T>);
end;

И вот такой дикий вызов мы в результате получаем Algo<TMyRec>.ForEach(MyList,DoClearMyRecord) Кстати этим же будет страдать любая попытка сделать обобщенный алгоритм средствами языка delphi.


4. Подход который я тестирую в настоящее время

Описываются курсоры двух типов:
TIterator<T> = class(TObject) - для одиночного значения
TRange<T>= class(TObject) - для диапазона значений (от А до Б, массив индексов и т.д.)

Все контейнеры наследуются от

 TIterable<T> = class abstract

  public

   function Iterator(const S:TIteratorState):TIterator<T>; overload; virtual; abstract;
   function Iterator(const I:Integer):TIterator<T>; overload; virtual; abstract;
   function Iterator(const V:T):TIterator<T>; overload; virtual; abstract;
//    function Range(const A,B:Integer):TRange<T>; overload; virtual; abstract;
//    function Range(const Indexes:array of Integer):TRange<T>; overload; virtual; abstract;

 end;

т.е. каждый контейнер должен уметь вернуть курсор (см. как в  Generics.Collections возвращается енумератор). При этом в курсор внедряются все необходимые данные, методы, эффективно переопределяются существующие методы с учетом местной специфики и т.п. (естественно все это не будет доступно в обобщенном алгоритме, кроме определенного в TIterator) Само объявление курсора примерно такое


 // uniform access to TContainer<T> via single item
 TIterator<T> = class(TObject)

  protected

   FFlags : TIteratorFlags;
   FInd   : Integer;
   FPtr   : Pointer_To_<T>.Ptr;

   function GetIsBack: Boolean;
   function GetIsBidirectional: Boolean;
   function GetIsFirst: Boolean;
   function GetIsForward: Boolean;
   function GetIsFront: Boolean;
   function GetIsItem: Boolean;
   function GetIsLast: Boolean;
   function GetIsRandom: Boolean;
   function GetItem: T;
   procedure SetIndex(const Value: Integer);
   procedure SetItem(const Value: T);

  public

   constructor Create;
   // positioning
   function Prev(const By:Integer=1):Integer; virtual;
   function Next(const By:Integer=1):Integer; virtual;
   // modify container storage
   function Insert(const Item:T):Integer; virtual; abstract;
   function Remove:Integer; virtual; abstract;
   // compare with other iterator
   function eq(const i:TIterator<T>):Boolean; virtual;
   function ab(const i:TIterator<T>):Boolean; virtual;
   function ls(const i:TIterator<T>):Boolean; virtual;
   function le(const i:TIterator<T>):Boolean; virtual;
   function ae(const i:TIterator<T>):Boolean; virtual;
   function dist(const i:TIterator<T>):Integer; virtual;
   // item access
   property Val:T read GetItem write SetItem;
   property Ptr:Pointer_To_<T>.Ptr read FPtr;
   property Ind:Integer read FInd write SetIndex;
   // state
   property IsForward:Boolean read GetIsForward;
   property IsBidirectional:Boolean read GetIsBidirectional;
   property IsRandom:Boolean read GetIsRandom;
   property Flags:TIteratorFlags read FFlags;
   property IsFront:Boolean read GetIsFront;
   property IsFirst:Boolean read GetIsFirst;
   property IsLast:Boolean read GetIsLast;
   property IsBack:Boolean read GetIsBack;
   property IsItem:Boolean read GetIsItem;

 end;


Тут есть проблемы с безопасностью курсора т.к. дается прямая ссылка на элемент в контейнере
property Ptr:Pointer_To_<T>.Ptr read FPtr;
С другой стороны это эффективный доступ к элементу и кроме того возможность использовать его как позицию (вместо индекса) для списков и деревьев.
Вторая плохая новость это то что итератор требует обобщенного типа, что не даст возможность объявить обобщенный алгоритм как

interface

procedure SomeAlgorithm(FromPos:TIterator; ToPos:TIterator; const Action:TActionFunc);

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


 
Cobalt ©   (2011-11-02 13:25) [2]

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


 
vlk32   (2011-11-02 14:18) [3]

Меня такой подход уже не устраивает. Потому что ничего путного по ходу дела не накрутится. Библиотеки с костылями у меня есть. Теперь же хочется изначально все спланировать.


 
jack128_   (2011-11-02 17:52) [4]


> Использование
> for_each (myvector.begin(), myvector.end(), myfunction);
>
>
> Обратите внимание - никакой информации об обобщенном типе
> в вызове for_each не используется. Возможно это тема для
> дискуссии, но мое мнение - реализовать подобную функциональность
> и синтаксис  в таком же примерно виде в delphi даже с использованием
> генериков и анономных процедур не представляется возможным


Обычному разработчику невозможно. А вот разработчикам компилятора вполне возможно, что они сделали встроив аналог for_each(цикл for .. in .. ) в язык.
Другое дело, что другие алгоритмы по аналогу с std конечно не получится реализовать, хотя бы из за отсутствия в delphi утиной типизации.


> Контейнеры унаследованы от некоего базового класса (TIterator),
>  который описывает максимально возможную функциональность
> итератора/курсора, т.е. возможность устанавливать курсор
> на требуемый элемент контейнера, перемещать курсор, читать/писать
> в элемент под курсором, вставлять/удалять элементы  т.д.
>  Каждый наследник переопределяет те функции курсора, которые
> он реально поддерживает, а на остальные вешает генерацию
> исключений.

ИМХО - отстойный вариант, потому что это по сути отход от статической типизации.

procedure MyFunc(Iter: TIterator<T>)
begin // на этапе компиляции - я не знаю, могу ли я добавлять/удалять элементы в контейнер.
end;


> Использование интерфейсов. Вся функциональность итератора/курсора
> разбивается на несколько интерфейсов по уровню функциональности
> (или какому то другому критерию). Все контейнеры которые
> мы хотим задействовать в обобщенных алгоритмах должны наследовать
> и реализовать интерфейсы курсора. В данном подходе никаких
> отдельных курсоров (в виде объектов или записей) не используется
.
>  

В C# не используется. Почему в дельфи нельзя использовать?


> Algo<T> = class
> class procedure ForEach(C:TContainer<T>; Action:TActionOverSingleItem<T>);
>
> end;
>
> И вот такой дикий вызов мы в результате получаем Algo<TMyRec>.
> ForEach(MyList,DoClearMyRecord) Кстати этим же будет страдать
> любая попытка сделать обобщенный алгоритм средствами языка
> delphi.


В теории можно написать так:
Algo2 = class
 class procedure ForEach<T>(C:TContainer<T>; Action:TActionOverSingleItem<T>);
end

вызов
Algo2.ForEach(C, Action);

А любая попытка сделать обобщенный алгоритм в дельфи будет страдать прежде всего от глюков компилера.


 
vlk32   (2011-11-02 19:59) [5]

Algo2 = class
class procedure ForEach<T>(C:TContainer<T>; Action:TActionOverSingleItem<T>);
end

хрен редьки не слаще. есть два варианта и оба они к сожалению беспонтовые

algo<integer>.reverse(mylist);

algo.reverse<integer>(mylist);

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

reverse(mylist);

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


 
jack128_   (2011-11-02 22:19) [6]


> algo.reverse<integer>(mylist);

ты уверен, что нужно писать именно так??
такой код: algo.reverse(mylist);
не работает??


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

меня  от написания своей библиотеки алгоритмов для коллекций остановило прежде всего внезапные глюки компилятора в неожиданных местах. Это я говорю по итогам написания light ORM, которую у нас в конторе написали. Наверно процентов 40% времени было потрачено на переделку кода, что бы он банально скомпилировался, банально всякие интернал ерроры запарили.

Ну и конечно неудобоваримый синтаксис лямбд тоже внес свою лепту.


 
vlk32   (2011-11-02 22:41) [7]

интернал ероры решаются периодическим жмаком по клавишам Shift+F9

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

let.foreach(a);

[DCC Error] test.pas(169): E2010 Incompatible types: "TBaseArray<test.let.foreach.T>" and "TBaseArray<System.Real>"

let.foreach<real>(a);

OKAY

так что я уверен что иначе - никак. компилятор требует точно указать тип в конце концов. по другому он пока не может.


 
_Юрий   (2011-11-03 17:07) [8]


> vlk32   (02.11.11 22:41) [7]
>


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


особенно весело бывает, когда проводишь рефакторинг, и проект пару дней вообще не собирается, а потом наконец то должен собраться - и нате вам, internal error, отменяйте последние изменения. На "пофиксит" надежда слабая.


> jack128_   (02.11.11 22:19) [6]


> меня  от написания своей библиотеки алгоритмов для коллекций
> остановило прежде всего внезапные глюки компилятора в неожиданных
> местах. Это я говорю по итогам написания light ORM, которую
> у нас в конторе написали. Наверно процентов 40% времени
> было потрачено на переделку кода, что бы он банально скомпилировался,
>  банально всякие интернал ерроры запарили.

Во, у нас тоже самое случилось.
И лямбды эмберкадеро сделали ужасно.  И мусор не собирается. И yield return нету. В общем, не взлетит.
Берем Generics.Collections и наслаждаемся тем, что имеем )


 
_Юрий   (2011-11-03 17:18) [9]


>  я пока до таких
> ситуаций не доходил


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


 
jack128_   (2011-11-04 00:14) [10]


> _Юрий   (03.11.11 17:18) [9]

кстати, а какой интерфейс для выборок у вас получился?? Мы в результате что то типа такого получили

for Obj in Storage.Select<TMyObject>("select ... ", True) do
begin
 
end; // если второй параметр - True, объект Obj сам уничтожится в конце итерации, если False - сами должны уничтожать.


Ну набор вспомогательных хелперов типа:

MyObjectList := Storage.SelectList<TMyObject>("select ...");
MyObj := Storage.SelectObject<TMyObject>("select ...");
for Obj in Storage.SelectAll<TMyObject>(True) do // имя таблицы берется из атрибута навешанного на класс TMyObject
и т д..


 
vlk32   (2011-11-04 00:22) [11]

Удалено модератором


 
vlk32   (2011-11-04 00:46) [12]

О ла ла. А а в XE2 все нормально. Это радует.


 
_Юрий   (2011-11-04 10:15) [13]


> jack128_   (04.11.11 00:14) [10]


> кстати, а какой интерфейс для выборок у вас получился??


В общем то, такой же. Только там вместо "select... " RTTI-шный построитель запроса на аттрибутах.


> vlk32   (04.11.11 00:46) [12]
> О ла ла. А а в XE2 все нормально. Это радует.
>
>


Вот так. Интересно. Мы на 2010 тренировались.


> class procedure let.foreach<T>(const C:TIterable<T>; const
> Action:TActionProc1<T>);

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

Сравните:

for X  in Container do
 Summa := Summa + X.Value;

или же

Container.ForEach(procedure(X: TMyObject)
begin
 Summa := Summa + X.Value;  
end);

второй вариант более громоздкий, следовательно он хуже.

А вот реализация энумератора должна быть своя для каждого типа контейнера, и пользователь должен быть абстрагирован от этой реализации.
В варианте цикла for in это как раз и достигается.


 
vlk32   (2011-11-04 11:27) [14]

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

Идем далее. Я например могу написать вот так


var a,b : TBaseArray<TMyRec>;

.. init a ..

b := let.extract<TMyRec>(a.range(MySelector));
let.foreach<TMyRec>(b.range(0,8),MyPerfectAction);



Вот и функция экстракт стала обобщенным алгоритмом - и ее теперь не надо тулить во все контейнеры. А ForEach уже умеет перебирать в заданном диапазоне. А диапазон может быть указан вот так


var indexes : Array_Of_<integer>;
     N : Integer;

b.range(iAll)
b.range(7,N)
b.range(Indexes)
b.range(function(const Item:TMyRec; const I:Integer):Boolean
            begin
             Result := (Item.MyField > N) and (Item.MyField2 in [colBlack,colRed,colGreen]);
            end;)



Удобно, правда?

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


 
_Юрий   (2011-11-04 14:29) [15]


> vlk32   (04.11.11 11:27) [14]



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


ну и в случае через GetEnumeator тоже самое - не обязательно все элементы возвращаются, зависит от реализации.
Объявить интерфейс IEnumerable<T> c методом GetEnumerator. В D2010 такой ход не прошел по причине ошибок компилятора
Если бы язык позволял делать хелперы к интерфейсам, то можно было бы раскрутиться отсюда, было бы все удобно. Но поддержки хелперов вроде не предвидится.

по поводу let.foreach<TMyRec>(b.range(0,8),MyPerfectAction);
Тут возникает единственный вопрос - кто управляет временем жизни этого самого результата от b.range? Это ведь некий контейнер, результат фильтрации изначального, очевидно, уже другой инстанс.
В этих ваших  сишарпах все просто - там сборщик мусора. А нам тут как быть?


 
Anatoly Podgoretsky ©   (2011-11-04 14:48) [16]

А вам бежать на C# или не делать странного.


 
vlk32   (2011-11-04 14:58) [17]

b.range(0,8) - да тут объект b создает новый объект типа TRange - обобщенное описание некоего подмножества элементов из b. Конечно сами элементы в этом новом объекте не хранятся. Т.к. этот объект нужен исключительно в обобщенном алгоритме то логично будет его в этом же алгоритме и освободить в конце когда он уже не нужен.


 
jack128_   (2011-11-04 23:03) [18]

Скажу как бы я делал, если бы не глюки компилятора, то есть в будущих(выше 2010) версиях такое( может быть) и прокатит.

Собственно - копия LINQ. Без хелперов к интерфейсам и дженерик классам - это выглядело бы примерно так:

ILinqEnumerable<T> = interface(IEnumerable<T>)
 function Select<T, TResult>(Selector: TFunc<T, TResut>): ILinqEnumerable<TResult>;
 function Where<T, T>(Predicte: TFunc<T, boolean>): ILinqEnumerable<T>;
 .... // и так далее
end;

TLinqEnumerableImpl<T> = class
 contructor Create(AEnum: IEnumerable<T>);
 function Select<T, TResult>(Selector: TFunc<T, TResut>): ILinqEnumerable<TResult>;
 function Where<T, T>(Predicte: TFunc<T, boolean>): ILinqEnumerable<T>;
// и так далее
end;

TMyList<T> = class(TInterfacedObject, IEnumerable<T>,
ILinqEnumerable<T>)
 private LinqEnum: ILinqEnumerable<T> read FLinqEnum implements ILinqEnumerable<T>; // FLinqEnum := TLinqEnumerableImpl<T>.Create(Self);
public
 property Items[Index: Integer]: T read; write;
end;


В зависимости от  реализации TLinqEnumerableImpl генерация последовательностей может быть ленивая или нет.

ну и пример использования:

MyList
 .Where(MyFilterFunc)
 .GroupBy(MyGroupProc)
 .Select(MySelectProc);


но естественно - все это не взлетит.  Синтаксис лямбд убог и с этин котжировцы ничего не хотят делать..


 
_Юрий   (2011-11-05 09:33) [19]


> А вам бежать на C# или не делать странного.


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


> vlk32   (04.11.11 14:58) [17]



> логично будет его в этом же алгоритме и освободить в конце
> когда он уже не нужен.


Как это будет выглядеть?
Вот, например, по [14]
var a,b : TBaseArray<TMyRec>;

.. init a ..

b := let.extract<TMyRec>(a.range(MySelector));
let.foreach<TMyRec>(b.range(0,8),MyPerfectAction);


Кто запоминает ссылку, кто ее потом освобождает, и в какой момент?
Согласитесь, этим должен заниматься движок, а не пользовательский код.


> jack128_   (04.11.11 23:03) [18]



>  Синтаксис лямбд убог и с этин котжировцы ничего не хотят
> делать..


А точно ли ничего не хотят делать? может, они просто не в курсе?


 
vlk32   (2011-11-05 11:00) [20]


> Кто запоминает ссылку, кто ее потом освобождает, и в какой
> момент?
> Согласитесь, этим должен заниматься движок, а не пользовательский
> код.



function let.extract<T>(const R:TRange<T>):TIterable<T>;
begin
if (R=Nil) or R.IsEmpty then Exit(Nil);
Result := R.Owner.Create(R);
R.Remove;
R.Free;
^^^^ R было создано прямо в вызове let.extract, у вызывающего нет на него ссылки поэтому надо освободить прямо здесь.
end;


Соглашения об ответственности за удаление временных объектов должно быть описано явно и являться частью библиотеки.

От интерфейсов сознательно отказался, потому что

- чистый цикл, время = 1
- foreach реализованный в контейнере (без итераторов), время = 2
- foreach через итераторы (никаких интерфейсов!), время от 5 до 7.5 (а может и до 10)

Куда еще дальше терять в производительности?


 
vlk32   (2011-11-05 11:02) [21]

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


 
jack128_   (2011-11-05 11:25) [22]


> А точно ли ничего не хотят делать? может, они просто не
> в курсе?

в курсе, я на конференции в Москве спрашивал об этом. Мне посоветовали Live Template (или там эта хрень называется) написать.


 
oxffff ©   (2011-11-05 11:32) [23]


> Собственно - копия LINQ


:)

Для этого нужно как минимум ввести в Delphi понятие анонимного типа.
И поработать над выводом типов в Delphi.


 
jack128_   (2011-11-05 14:24) [24]


> Для этого нужно как минимум ввести в Delphi понятие анонимного
> типа.

Для LINQ to Objects - это совсем не критично. ИМХО, анонимные типы для LINQ To SQL прежде всего сделаны.


> И поработать над выводом типов в Delphi.

Ну конечно было бы не плохо, но главное - синтаксис лямбд.


 
Kerk ©   (2011-11-06 01:46) [25]


> jack128_   (02.11.11 17:52) [4]
>
> Другое дело, что другие алгоритмы по аналогу с std конечно
> не получится реализовать, хотя бы из за отсутствия в delphi
> утиной типизации.

В Delphi Prism оно есть. Может, когда-нибудь и в обычном сделают.


 
Kerk ©   (2011-11-06 01:48) [26]

Хм...
http://tzblok.blogspot.com/2011/08/duck-typing-v-delphi.html


 
Kerk ©   (2011-11-06 01:57) [27]

Вообще, я очень давно таких содержательных обсуждений не видел :)
Интересно.


 
vlk32   (2011-11-06 02:27) [28]

Посмотрел пример про утку и персону. Оригинально. Но вряд ли применимо на практике. Например я добавил в метод кваканья несколько параметров. А вот здесь оставил все как было:


procedure InTheForest(V: Variant);
begin
V.Quack; <<< теперь в этом методе есть параметры но здесь они не указаны
V.Feathers;
end;


Вопрос. Что будет если дело дойдет до Quack и он начнет обрабатывать параметры которые ему даже не передали? Но при этом все компилируется. Даже варнингов нет. Короче идеальная система для того чтобы прострелить себе обе ноги, и в конце концов голову.


 
jack128_   (2011-11-06 02:35) [29]


> Хм...
> http://tzblok.blogspot.com/2011/08/duck-typing-v-delphi.
> html

Это еще времен дельфи 7(а то и раньше, не в курсе) техника.  Естественно нужна диспетчеризация в компил тайм.


 
jack128_   (2011-11-06 02:36) [30]


> Естественно нужна диспетчеризация в компил тайм.

Ну то есть что значит нужна? Естественно всегда можно все проверки в рантайм вынести. Только нафиг тогда дельфи, если есть  javaScript ?


 
_Юрий   (2011-11-06 11:33) [31]


> vlk32   (05.11.11 11:02) [21]


>  Можно принять соглашение что все итераторы поступившие
> в функцию обобщенного алгоритма будут гарантировано в нем
> освобождены.

А что делать, если пользователь передал туда итератор, который ему еще нужен?


 
vlk32   (2011-11-06 11:51) [32]


> А что делать, если пользователь передал туда итератор, который
> ему еще нужен?


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

var i : TRange<MyRec>;
    j : Array_Of_<Integer>;
 
let.extract(a.range(0,a.Count div 2)); <<< range будет уничтожен в let.extract

i := a.range(iAll,False); <<< создали итератор со сброшенным флагом авто-уничтожения
i.setup(j);
let.extract(i);
i.setup(iAll);<<< итератор все еще доступен
i.Free; <<< мы должны освободить его сами


 
_Юрий   (2011-11-06 13:35) [33]


> vlk32   (06.11.11 11:51) [32]
>


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


 
vlk32   (2011-11-06 15:26) [34]

В принципе согласен. С удовольствием попробовал бы сделать либу на интерфейсах, но пока отложу эту идею в сторону.


 
jack128_   (2011-11-06 21:24) [35]

Внезапно такой вот код нарылся:

http://code.google.com/p/delphi-spring-framework/source/browse/trunk/Source/System/Collections/Spring.Collections.pas


 
vlk32   (2011-11-06 22:05) [36]

Да про эту либу слышал краем уха. Порт (повторение?) джавовской либы в делфях. Вот только ее версия сильно смущает. Оно вообще доведено до логического конца?


 
jack128_   (2011-11-06 22:15) [37]

Ну я не джавист, но судя по тому, что в Collections реализован именно LINQ, то вряд ли это чистый порт джавы.
Ну и насколько это production ready либа - тоже сказать не могу, только пару часов назад как нарыл её..
Я так, чисто в качестве примера линк дал...


 
oxffff ©   (2011-11-08 11:10) [38]

Еще одна реализация duck typing

http://www.delphifeeds.com/go/s/86051



Страницы: 1 вся ветка

Текущий архив: 2014.01.19;
Скачать: CL | DM;

Наверх




Память: 0.62 MB
Время: 0.008 c
15-1375043402
Юрий
2013-07-29 00:30
2014.01.19
С днем рождения ! 29 июля 2013 понедельник


8-1233859275
Arslan
2009-02-05 21:41
2014.01.19
Конвертирование wav в mp3


15-1375043600
Petr
2013-07-29 00:33
2014.01.19
DB/2. ацтой или рай?


1-1320565646
Remad
2011-11-06 10:47
2014.01.19
GETMEM.INC


2-1363761523
Андрей2000
2013-03-20 10:38
2014.01.19
Не работает кнопка