Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2014.01.19;
Скачать: [xml.tar.bz2];

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.61 MB
Время: 0.004 c
15-1373539892
[ВладОшин]
2013-07-11 14:51
2014.01.19
нечеткий custom датасет


15-1375123950
Rouse_
2013-07-29 22:52
2014.01.19
Отдам в хорошие руки


15-1375543089
Anatoly Podgoretsky
2013-08-03 19:18
2014.01.19
Test


15-1374908638
Пит
2013-07-27 11:03
2014.01.19
Сумасшедший радиоуправляемый вертолет


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





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский