using LINQ to find the cumulative sum of an array of numbers in C#
我有一个包含双精度的csv字符串(例如"0.3,0.4,0.3"),我希望能够输出包含这些数字的累积和的双精度数组(例如[0.3,0.7,1.0])。
到目前为止,我有
它以数组的形式给出数字,但不是数字的累积和。
是否有任何方法可以继续此表达式以获取所需的内容,或者是否需要使用迭代从已具有的数组创建新的数组?
有一个通用的时间,有一个时间来解决实际提出的问题。这是后一次。如果要生成一个将双精度数序列转换为部分和序列的方法,请执行以下操作:
1 2 3 4 5 6 7 8 9 | public static IEnumerable<double> CumulativeSum(this IEnumerable<double> sequence) { double sum = 0; foreach(var item in sequence) { sum += item; yield return sum; } } |
容易的。不要在聚合、复杂的查询和其他方面搞得一团糟。易于理解、易于调试、易于使用:
1 2 3 4 5 |
现在,我注意到,如果这是用户输入,那么double.parse可以抛出一个异常;最好这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static double? MyParseDouble(this string s) { double d; if (double.TryParse(s, out d)) return d; return null; } public static IEnumerable<double?> CumulativeSum(this IEnumerable<double?> sequence) { double? sum = 0; foreach(var item in sequence) { sum += item; yield return sum; } } ... textBox_f.Text .Split(new char[]{','}) .Select(s => s.MyParseDouble()) .CumulativeSum() .ToArray(); |
现在,如果用户输入错误,您不会得到异常;您会得到空值。
我以前也有过类似的要求。基本上,我需要进行聚合,但我也需要选择每个中间值。所以我写了一个扩展方法,名为
1 2 | double[] numbers = new [] { 0.3, 0.4, 0.3 }; double[] cumulativeSums = numbers.SelectAggregate(0.0, (acc, x) => acc + x).ToArray(); |
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static IEnumerable<TAccumulate> SelectAggregate<TSource, TAccumulate>( this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) { source.CheckArgumentNull("source"); func.CheckArgumentNull("func"); return source.SelectAggregateIterator(seed, func); } private static IEnumerable<TAccumulate> SelectAggregateIterator<TSource, TAccumulate>( this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) { TAccumulate previous = seed; foreach (var item in source) { TAccumulate result = func(previous, item); previous = result; yield return result; } } |
您希望使用
下面是一个让您开始的示例:
1 2 3 4 5 6 |
首先,我不认为这对Linq来说是一个好任务。普通老的
第一个想法是使用子查询,但我不喜欢它,因为它是O(n^2)。这是我的线性解:
1 2 3 4 5 6 7 8 9 10 |
为什么需要是Linq?
1 2 3 | var cumulative = new double[probabilities.Length]; for (int i = 0; i < probabilities.Length; i++) cumulative[i] = probabilities[i] + (i == 0 ? 0 : cumulative[i-1]); |
使用RX:
1 2 3 |
这实际上是非常简单的概括使用生成器。这里有一个名为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static class EnumerableHelpers { public static IEnumerable<U> Accumulate<T, U>(this IEnumerable<T> self, U init, Func<U, T, U> f) { foreach (var x in self) yield return init = f(init, x); } public static IEnumerable<T> Accumulate<T>(this IEnumerable<T> self, Func<T, T, T> f) { return self.Accumulate(default(T), f); } public static IEnumerable<double> PartialSums(this IEnumerable<double> self) { return self.Accumulate((x, y) => x + y); } public static IEnumerable<int> PartialSums(this IEnumerable<int> self) { return self.Accumulate((x, y) => x + y); } } |
下面是一种使用LINQ的方法:
1 2 3 4 5 6 7 8 9 10 | double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 }; var doublesSummed = new List<double>(); Enumerable.Aggregate(doubles, (runningSum, nextFactor) => { double currentSum = runningSum + nextFactor; doublesSummed.Add(currentSum); return currentSum; }); doublesSummed.Dump(); |
在LINQPad:
- 四
- 五点九
- 十
- 十二点九
1 2 3 4 5 |