Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4460.html
 

Ограничения для обобщений

Опубликовано 25.02.2011 г. 19:12

Попалась на работе задача, которую, возможно было бы удобно решать с помощью обобщений. Но вот в конкретной реализации возникают вопросы.

В чем же задача состоит? Есть набор данных, некоторый числовой ряд, пары значений (x,y). Для определенности будем считать, что Х это годы, а Y значения. При этом Y могут быть как целые значения так и дробные, т.е либо integer либо real. Теперь непосредственно задача: для ряда необходимо строить некоторые тренды, пусть для простоты линейный тренд. С этой точки зрения мне без разницы с какими данными я буду работать для вычисления точек тренда, целыми или нет. Рассмотрим задачу более широко, и она будет представлена двумя частями: представление данных над которыми необходимо провести действия, и алгоритмы проведения этих действий. Для использования в отельной библиотеке определим простые интерфейсы для хранения данных по типам int & float:
    IDMValues = interface (IInterface)
        procedure Clear(); safecall;
    end;

    IDMIntValues = interface(IDMValues)
        procedure Add(const x : integer; const y : integer);    safecall;
    end;

    IDMFloatValues = interface(IDMValues)
        procedure Add(const x : integer; const y: real);    safecall;
    end;
Т.е. мы можем получить объект необходимого типа, и наполнить его данными, ну или очистить для повторного использования. Теперь объекты которые строят тренды: базовый имеет возможность устанавливать объект исходных данных и выполнят метод построения с параметрами по умолчанию.
    IDMApproximation = interface(IInterface)
        procedure SetDMValues(values : IDMFloatValues); safecall;
        procedure Calculate(); safecall;
    end;

    IDMLinearApproximation = interface(IDMApproximation)
         ....
    end;
Перейдем непосредственно к реализации объектов для хранения данных, здесь нам потребуется один обобщенный объект, в котором мы можем заменить типы данных для Y на Т. На этом уровне нам безразлично какой тип это будет. И два класса с непосредственной поддержкой интерфейсов IntValues & FloatValues.
    TDMCustomValues<T>= class(TInterfacedObject, IDMValues)
      strict private
        const FDefaultCapacity = 20;
      protected
        FData : TDictionary<integer, T>;
      public
        constructor Create();
        destructor Destroy(); override;
        procedure Clear(); safecall;
        procedure Add(const x : integer; const y : T);    safecall;
    end;

    TDMIntValues = class(TDMCustomValues<integer>, IDMIntValues)
    end;

    TDMFloatValues = class(TDMCustomValues<real>, IDMFloatValues)
    end;
Да, конечные классы не содержат никаких методов и свойств, им это и не требуется, все определено в базовом обобщении. Теперь наступает самый интересный момент. Как видите у обобщенного класса TDMCustomValues установлено ограничение record. Это означает, что в качестве параметра можно использовать перечисляемые типы и записи. Данное ограничение подразумевает (как мне кажется(: ), что с его использованием мы можем проводить операции, например, сложения над параметрами. Почему? int/float мы и так можем складывать, а для любой записи (record) мы можем переопределить операцию сложения (class operator Add). Но вместо этого попытавшись сложить два аргумента типа T мы получаем ошибку:
[DCC Error] DMClasses.pas(68): E2015 Operator not applicable to this operand type
А почему в общем то так? Тип данных операцию сложения поддерживает, ее для записей разве что реализовать надо. Было бы куда логичней при попытке использования TDMCustomValues<TMyRecord> говорить о том, что тип TMyRecord не поддерживает операцию сложения. Вот собственно и вопрос, а как вобще реализовывать различные алгоритмы, когда аргументами могут выступать как целые, так и числа с плавающей запятой? Либо пренебречь оптимизаций (вычисления над int все таки наверное выполняются быстрее чем над real) и свести все к real отказавшись от int, либо реализовать два расчетных метода для int & float отдельно (возможно основываясь на типах данных и реализация будет отличаться). Кто как решает подобные вопросы?
Метки:  generics 

Комментарии

Дмитрий
26.02.2011 в 12:48
>>Как видите у обобщенного класса TDMCustomValues установлено ограничение record.
Нет, лично я не вижу.
Как вариант использовать TDictionary - словать год/значение или даже TDictionary, и собственно TKeyValuePair.
Этот способ может оказаться также непроизводительным, но зато и бескостыльным. Во всяком случае TValue нам предлагают как раз для решения таких вот проблем, так почему бы этим не воспользоваться? В любом случае лучше провести замеры условиях боевых действий.
ter
26.02.2011 в 13:40
не понял вас видимо.
там и так используется TDictionary<integer,TValue> а TDMCustomValues является лишь оберткой для поддержки IDMValues.
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно