Есть ли в С# интерфейс для интервальных значений?

Я делаю расширение коллекции интервалов известной библиотеки C# C5. Интерфейс IInterval определяет интервал с сопоставимыми конечными точками (удалены нерелевантные элементы):

public interface IInterval<T> where T : IComparable<T>
{
    T Low { get; }
    T High { get; }
}

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

Однако иногда полезно иметь возможность рассчитать продолжительность интервала. Интервал [3:5) имеет продолжительность 2, а интервал [1PM, 9PM) имеет продолжительность 8 часов. Это невозможно с сопоставимыми, поскольку это дает нам только порядок элементов, а не их расстояние, например. трудно определить расстояние между двумя строками. Тип конечной точки в основном должен быть интервальными значениями.

Есть ли такой интерфейс, как IComparable<T>, который позволяет мне сравнивать конечные точки в целом, а также делать такие вещи, как вычитание двух конечных точек, чтобы получить длительность, и добавление длительности к нижней конечной точке, чтобы получить верхнюю конечную точку, которую можно использовать для наследующего интерфейса? , IDurationInterval<T> : IInterval<T> например?

Или, более кратко: есть ли интерфейс для интервальных значений?


person Mikkel R. Lund    schedule 16.07.2015    source источник


Ответы (1)


Такого интерфейса не существует. В некоторых языках, таких как Scala, есть абстракции над такими операциями, но в .NET их нет. Кроме того, я обнаружил, что разработчики библиотек .NET довольно консервативны, когда дело доходит до добавления уровней абстракции: похоже, они предпочитают простые и конкретные сложные и абстрактные. Вероятно, они никогда не добавляли эту абстракцию, потому что в ней никогда не было острой необходимости ни в одной из библиотек .NET Framework. Кроме того, это абстракция, которая может вызвать много накладных расходов при неправильном использовании.

Однако ничто в системе типов .NET не препятствует такому интерфейсу. Ему потребуются два параметра типа вместо одного: один для типа реализации и один для типа результата. Самый простой интерфейс может выглядеть так:

interface IAlgebraic<T, R> {
  T Add(R value)
  R Subtract(T value)
}

К сожалению, нет возможности добавить этот интерфейс к существующим типам, таким как Int32 и DateTime. Для этих типов вам понадобится следствие IComparable<T> Comparer<T>.

interface IAlgebraicOps<T, R> {
  T Add(T x, R y)
  R Subtract(T x, T y)
}
person Nimrand    schedule 19.07.2015
comment
Это также моя лучшая идея на данный момент. Меня беспокоит то, что это добавит слишком много накладных расходов, но в этом может быть достаточно смысла. Вероятно, вы могли бы создать интерфейс, подобный IDurationInterval<T, R>, с помощью методов T Add(R difference), T Subtract(R difference) и R Difference(T other). Это, однако, все еще имеет свои недостатки, так как вы все равно не сможете найти среднее значение интервала. Но мне нравится общая идея! - person Mikkel R. Lund; 20.07.2015
comment
Накладные расходы на вызов виртуального метода через интерфейс велики только по сравнению с простой инструкцией добавления процессора, которую можно использовать для простых типов данных, таких как целые числа. Если вы не собираетесь выполнять миллионы вычислений в секунду, я сомневаюсь, что вы заметите разницу. - person Nimrand; 20.07.2015
comment
Упоминание о средней точке поднимает еще один хороший момент. Хотя вы можете абстрагироваться от математических операций, таких как сложение и вычитание, трудно понять, где остановиться. Общий интерфейс Scala, Numeric, имеет операции для сравнения, сложения, вычитания, умножения, абсолютного, единичного и нулевого. Int, Long, Float и Double могут поддерживать все это, но только первые 3 имеют смысл для DateTime. На самом деле можно создать иерархию интерфейсов. В его основе Comparable. Есть интерфейс, поддерживающий сложение, вычитание и подобные операции. - person Nimrand; 20.07.2015
comment
Вдобавок к этому можно вычислить среднюю точку, используя этот интерфейс, при условии, что R поддерживает операцию деления (через второй интерфейс, например, IDivisible). Чтобы вычислить среднюю точку, вы просто используете interval.Low.Add(interval.High.Subtract(interval.Low).Divide(2)). Если вы предпочитаете, я могу уточнить это в своем ответе, но ваш первоначальный вопрос не включал эту возможность. - person Nimrand; 20.07.2015
comment
Вы совершенно правы; это вопрос того, где вы хотите остановиться. Хотя я думаю, что вы могли бы далеко продвинуться с основными операциями, которые я перечислил выше. Чтобы вернуться к проблеме производительности: если вам нужно разделить, например, DateTime, вам придется обернуть его интерфейсом для деления, что даст вам еще одну степень косвенности. Я боюсь, что эти вещи быстро складываются. Я видел среднюю точку, используемую для бинарного поиска в DateTimes, так что это не невообразимо, но, возможно, не очень полезно в общей библиотеке... - person Mikkel R. Lund; 20.07.2015
comment
Абстрактная алгебра фактически уже проанализировала и очертила возможные абстракции с большим количеством деталей. Но я думаю, что для большинства практических целей иерархия интерфейса IComparable › IAddSubtract › INumeric (для умножения, нуля, единицы и абсолютного значения) › IDivisible (для поддержки деления) захватывает почти все, что может понадобиться (за исключением лучших имен). - person Nimrand; 20.07.2015
comment
Я не знаю, боюсь ли я, что вещи быстро складываются. Вы говорите о линейном увеличении накладных расходов, что по большому счету ничтожно мало. Временная сложность задействованных алгоритмов не пострадает. Например, поиск в наборе из 65 536 элементов требует не более 16 вычислений средней точки. Это 16 * 3 = 48 вызовов виртуального интерфейса. Это ничего. - person Nimrand; 20.07.2015
comment
Тем не менее, я все еще не совсем понимаю, чего вы надеетесь достичь, используя интервалы и коллекции. Вы определили интервал как в основном диапазон заданного набора значений (например, Ints, DateTimes и т. д.). Как это связано с коллекциями? Я могу придумать множество способов их совместного использования, но я не могу сказать, к какому из них вы клоните. Контекст важен при рассмотрении производительности. Как вы ожидаете, что ваша библиотека будет использоваться, и каковы требования к производительности? - person Nimrand; 20.07.2015
comment
Библиотека содержит наборы интервалов, которые в основном представляют собой наборы интервалов, которые позволяют быстро получить или подсчитать все интервалы в наборе, перекрывающие точку/интервал. Например, это можно использовать в календаре, чтобы найти все события, происходящие сегодня. Детали интервалов можно абстрагировать для многих операций с самими интервалами, а не с коллекциями. Таким образом, такие операции, как Overlaps или Contains, реализованы в статическом классе расширения, поэтому пользователи могут использовать его в своих собственных реализациях IInterval<T>. Но другим алгоритмам может понадобиться продолжительность. - person Mikkel R. Lund; 20.07.2015
comment
Давайте продолжим обсуждение в чате. - person Nimrand; 20.07.2015
comment
Re: К сожалению, нет возможности добавить этот интерфейс к существующим типам. Это, безусловно, правда. Конечно, эту проблему можно решить с помощью другого уровня косвенности: Где введите T не реализует этот интерфейс, напишите для него оболочку типа TProxy, которая реализует интерфейс. Добавьте оператор неявного приведения типа к TProxy из T, и вы почти у цели. - person stakx - no longer contributing; 20.07.2015
comment
Я начал работать над интерфейсом, когда меня внезапно осенило, что конечные точки должны быть значениями, масштабируемыми по интервалу: en.wikipedia.org/wiki/Level_of_measurement#Interval_scale. Я обновил вопрос соответственно. - person Mikkel R. Lund; 05.08.2015