C# 如何在Linq查询中计算一系列整数的运行和?
我试图提出一个linq查询,将一个C# 如何在Linq查询中计算一系列整数的运行和?,c#,linq,C#,Linq,我试图提出一个linq查询,将一个IEnumerable转换为另一个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[]序列,我认为如果我继续查询,而
IEnumerable
转换为另一个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[]序列,我认为如果我继续查询,而不是突然在那里为添加,)好吧,你可以很容易地完成它,但会有副作用,尽管它很讨厌
int sum = 0;
int[] b = a.Select(x => (sum += x)).ToArray();
如果框架提供了一种“运行聚合”来封装这一点,那就太好了,但据我所知,它并没有。这类似于Haskell的函数
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();
公共静态IEnumerable扫描(
这是一个数不清的来源,
Func(联合收割机)
{
使用(IEnumerator data=source.GetEnumerator())
if(data.MoveNext())
{
T first=数据。当前;
收益率优先;
while(data.MoveNext())
{
first=联合收割机(first,data.Current);
收益率优先;
}
}
}
int[]b=a
.扫描((运行,当前)=>运行+当前)
.ToArray();
替代Skeet先生的解决方案:如果我们放弃linq查询的要求,更确切地说是“将一个IEnumerable
转换为另一个IEnumerable
”,我们可以使用以下方法:
static IEnumerable<int> Sum(IEnumerable<int> a)
{
int sum = 0;
foreach (int i in a)
{
sum += i;
yield return sum;
}
}
如果您不想一次创建整个阵列,这也很有用。上述答案不太有效……并且与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;
}
}
}
你真是太棒了:)是的,它有点讨厌,但它已经足够好了,我最近正在寻求删除for语句。是的,我使用数组语法是因为它更容易解释需求,但该参数实际上是一个IEnumerable。但是,Skeet先生在这种情况下仍然可以工作,只是没有.ToArray()调用,而且它更紧凑,所以我仍然投票支持:)您在中遇到了一个问题,即首先返回,如果是类型T,则首先返回,而不是TResult
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);
}