Re[38]: «Собаку съел»
От: vdimas Россия  
Дата: 16.01.17 12:55
Оценка:
Здравствуйте, Слава, Вы писали:

V>>Т.е., те фишки ФП, которые не руинят ООП — они в практику дотнета таскаются с удовольствием, смотрю. Остальные практики игнорятся, хотя именно такая реализация параметрического полиморфизма требует именно таких практик. ))

С>Я вот не понимаю, о какой технике речь. Поясните, пожалуйста. На примере.

"Словарь операций" умеет работать с некими типами или классом типов. Для статического ресолвинга в дотнете словарь может быть только value-типом.

Возьмем тот же компаратор и метод Sort(IComparer<T> comparer) какого-нить List<T>, изменим сигнатуру метода:
public class List<T> {
  public void Sort<Comparer>(Comparer comparer) where Comparer : IComparer<T> {
    ...
  }
  ...
}

В этом случае можно реализовать Comparer для произвольного типа T, причем сам тип T можно не нагружать реализацией интерфейса IComparable<T>. Если Comparer будет выполнен как value-type, то JIT будет вынужден сгенерировать уникальную реализацию метода Sort, в итоге мы избавляемся от вызовов методов интерфейса IComparer<T>, т.е. от лишней динамики, начинает работать статика и прочий инлайнинг.

Т.е., суть техники в том, что можно разработать некий тип и рядом с ним "словарь" операций над ним.
Схематично:
interface IMath<T>
{
  T Add(T a, T b);
  T Sub(T a, T b);
  T Mul(T a, T b);
  T Div(T a, T b);
}

struct VulgarFraction {
  public int Numerator, Denominator;

  public struct Operations : IMath<VulgarFraction>
                           , IEqualityComparer<VulgarFraction>
                           , IComparer<VulgarFraction>
                           , ...
 {
    public VulgarFraction Mul(VulgarFraction a, VulgarFraction b) {
      return new VulgarFraction { Numerator = a.Numerator*b.Numerator, Denominator = a.Denominator*b.Denominator }; 
    }
    ...
  }
}


Далее. Самих таких "словарей операций" для некоего типа может быть более одного. Например, для простой дроби — с упрощением после каждой операции или без упрощения. С проверкой на переполнение int или без и т.д.

Далее один из таких словарей можно подавать на методы, организованные по принципу доработанной сигнатуры Sort выше.

Например, для встроенных числовых типов дотнета нет никаких ограничений, выраженных в виде интерфейса, позволяющих использовать математические операции, но можно нарисовать для каждого интересующего типа тот самый "словарь операций", реализующий IMath<> и, таким образом, разрабатывать эффективные математические generic-алгоритмы. Разве что потребуется каждый раз подавать этот словарь как аргумент — либо как обычный аргумент, либо как генерик параметр примерно так:

public class OurExtentions {
  public T Average<Operations, T>(IEnumerable<T> collection) where Operations : IMath<T>, new() {
    Operations ops = new Operations();
    ...
      sum = ops.Add(sum, item);
    ...
  }
}


Ну или можно представить некий сложный алгоритм как объект (структуру) и параметризировать этот объект словарём лишь однажды.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.