в функциональном отношении это комбинация:
молния
взять две последовательности и создать последовательность кортежей из элементов
и
карта
Возьмите функцию f
и последовательность и верните новую последовательность, которая является f (x) для каждого x в исходной последовательности
Почтовый индекс тривиален в c # 4.0
Взяв упрощенную реализацию, мы имеем
static class Enumerable
{
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> func)
{
var ie1 = first.GetEnumerator();
var ie2 = second.GetEnumerator();
while (ie1.MoveNext() && ie2.MoveNext())
yield return func(ie1.Current, ie2.Current);
}
}
Тогда нам нужна карта. У нас это уже есть, это то, что мы называем Select в c #
IEnumerable<int> input = { 1,2,3,4 };
int a = 0;
var accumulate = input.Select(x =>
{
a += x;
return a;
});
Но безопаснее сделать это в своем собственном методе (без каррирования в c #) и разрешить поддержку произвольных типов / накоплений.
static class Enumerable
{
public static IEnumerable<T> SelectAccumulate<T>(
this IEnumerable<T> seq,
Func<T,T,T> accumulator)
{
var e = seq.GetEnumerator();
T t = default(T);
while (e.MoveNext())
{
t = accumulator(t, e.Current);
yield return t;
}
}
}
Тогда мы можем собрать их вместе, как это так
var input = new int[] {1,2,3};
var mapsum = input.Zip(
input.SelectAccumulate((x,y) => x+y),
(a,b) => new {a,b});
Это будет повторять последовательность дважды, но является более общим. Вы можете сделать аккумулятор самостоятельно в рамках стандартного выбора и простого замыкания, но он больше не так полезен, как «строительный блок», являющийся одной из движущих сил функционального программирования.
Поддержка кортежей - это боль, кроме как внутри метода, поскольку анонимные типы не пересекают границы метода без особых хлопот. Несколько основных кортежей должны быть включены в c # 4.0. предполагая класс / структуру кортежа с именем Pair<T,U>
, вы можете сделать:
public static IEnumerable<Pair<T,T>> ZipMapAccumulate<T>(
this IEnumerable<T> input,
Func<T,T,T> accumulator)
{
return input.Zip(
input.SelectAccumulate((x,y) => accumulator (x,y)),
(a,b) => new Pair<T,T>(a,b));
}
//get an int specific one
public static Func<IEnumerable<int>, IEnumerable<Pair<int,int>>>
ZipMapSum()
{
return input => Enumerable.ZipMapAccumulate(
input,
(i,j) => i + j);
}
Там, где c # linq становится гораздо более громоздким, чем такие языки, как f #, это плохая поддержка операторов, карри и кортежей, если вы не храните все внутри одной функции и не «воссоздаете ее» каждый раз для каждого типа.