mwa*_*s42 6 c# linq functional-programming
给定一个对象列表,我需要返回一个列表,该列表包含对象和目前为止所见列表中所有对象的对象属性的总和.
更一般地给出
var input = new int[] {1,2,3}
Run Code Online (Sandbox Code Playgroud)
我想有输出
// does not compile but did not want to include extra classes.
var output = { (1,1), (2,3), (3,6) };
Run Code Online (Sandbox Code Playgroud)
这种"正确"的功能方式是什么?我当然可以用标准的迭代方法来实现它,但我正在寻找如何以功能性,懒惰的方式完成它.
谢谢
我认为这是最短的方法:
int sum = 0;
var result = input.Select(i => new { i, S = sum += i });
Run Code Online (Sandbox Code Playgroud)
在功能方面,这是以下组合:
取两个序列并创建一系列元素元组
和
取一个函数f和一个序列,并为原始序列中的每个x返回一个新的序列f(x)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们需要地图.我们已经拥有它,这就是我们在c#中所谓的Select
IEnumerable<int> input = { 1,2,3,4 };
int a = 0;
var accumulate = input.Select(x =>
{
a += x;
return a;
});
Run Code Online (Sandbox Code Playgroud)
但是把它加入到它自己的方法中更安全(在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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以把它们放在一起
var input = new int[] {1,2,3};
var mapsum = input.Zip(
input.SelectAccumulate((x,y) => x+y),
(a,b) => new {a,b});
Run Code Online (Sandbox Code Playgroud)
这将迭代序列两次,但更通用.您可以选择在标准选择和简单闭包中自己执行累加器,但它不再像"构建块"那样有用,它是功能编程背后的驱动力之一.
除了一个方法之外,元组支持是一种痛苦,因为匿名类型不会在没有相当麻烦的情况下遍历方法边界.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);
}
Run Code Online (Sandbox Code Playgroud)
其中c#linq变得比f#更加繁琐是对运算符,currying和元组的不良支持,除非你将所有内容保存在一个函数中并且每次为每种类型"重建它".