我正在尝试使用linq查询将IEnumerable<int>
转换为另一个IEnumerable<int>
,其中结果中的每个int都是从初始列表到该位置的所有整数的总和:
鉴于int[] a
我需要int[] b
b[0] = a[0], b[1] = a[0] + a[1], b[2] = a[0] + a[1] + a[2]
等等
或者,上面的总和可以写成b[1] = b[0] + a[1], b[2] = b[1] + a[2]
,依此类推,但我不知道这会有什么帮助。
当然,我可以使用for
循环执行此操作,但是我从查询中获取a []序列,我认为如果我继续查询而不是突然添加{{{ 1}}那里:))
答案 0 :(得分:15)
嗯,你可以很容易地做副作用,虽然它很蠢......
int sum = 0;
int[] b = a.Select(x => (sum += x)).ToArray();
如果框架提供了一种“运行聚合”来封装它,那将是很好的,但它并不是我所知道的。
答案 1 :(得分:8)
我刚才写了一个函数来做这件事。它类似于Haskell的scanl函数。
public static IEnumerable<TResult> Scan<T, TResult>(
this IEnumerable<T> source,
Func<T, T, TResult> combine)
{
using (IEnumerator<T> data = source.GetEnumerator())
if (data.MoveNext())
{
T first = data.Current;
yield return first;
while (data.MoveNext())
{
first = combine(first, data.Current);
yield return first;
}
}
}
int[] b = a
.Scan((running, current) => running + current)
.ToArray();
答案 2 :(得分:5)
Skeet先生解决方案的替代方案:如果我们放弃了对linq查询的要求,并且更确切地说是“将IEnumerable<int>
转换为另一个IEnumerable<int>
”,我们可以使用:
static IEnumerable<int> Sum(IEnumerable<int> a)
{
int sum = 0;
foreach (int i in a)
{
sum += i;
yield return sum;
}
}
我们可以应用于无限系列:
foreach (int i in Sum(MyMath.NaturalNumbers))
Console.WriteLine(i);
如果您不想一次创建整个数组,这也很有用。
答案 3 :(得分:0)
上面的答案并不完全正常....并且与Haskell的scanl没有共享相同的签名.... 基本上它是对linq“聚合”概念的扩展......我认为这更符合Haskell的实现
public static IEnumerable<TResult> Scanl<T, TResult>(
this IEnumerable<T> source,
TResult first,
Func<TResult, T, TResult> combine)
{
using (IEnumerator<T> data = source.GetEnumerator())
{
yield return first;
while (data.MoveNext())
{
first = combine(first, data.Current);
yield return first;
}
}
}
使用
[TestMethod]
public void Scanl_Test()
{
var xs = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var lazyYs = xs.Scanl(0, (y, x) => y + x);
var ys = lazyYs.ToArray();
Assert.AreEqual(ys[0], 0);
Assert.AreEqual(ys[1], 1);
Assert.AreEqual(ys[2], 3);
Assert.AreEqual(ys[3], 6);
Assert.AreEqual(ys[4], 10);
Assert.AreEqual(ys[5], 15);
Assert.AreEqual(ys[6], 21);
Assert.AreEqual(ys[7], 28);
}