使用LINQ查找C#中数字数组的累积和

sim*_*005 19 c# linq arrays cumulative-sum

我有一个包含双精度的csv字符串(例如"0.3,0.4,0.3"),我希望能够输出一个包含这些数字累积和的双数组(例如[0.3,0.7,1.0]).

到目前为止,我有

double[] probabilities = textBox_f.Text.Split(new char[]{','}).Select(s => double.Parse(s)).ToArray();

它将数字作为数组给出,但不是数字的累积和.

有没有办法继续这个表达式来获得我想要的东西,还是我需要使用迭代从我已经拥有的数组创建一个新的数组?

Eri*_*ert 46

有一般性的时间,并且有时间解决实际提出的问题.这是后者之一.如果你想创建一个方法,将一系列双精度变成一个部分和的序列,那么就这样做:

public static IEnumerable<double> CumulativeSum(this IEnumerable<double> sequence)
{
    double sum = 0;
    foreach(var item in sequence)
    {
        sum += item;
        yield return sum;
    }        
}
Run Code Online (Sandbox Code Playgroud)

简单.没有弄乱聚合和复杂的查询和诸如此类的东西.易于理解,易于调试,易于使用:

textBox_f.Text
    .Split(new char[]{','})
    .Select(s => double.Parse(s))
    .CumulativeSum()
    .ToArray();
Run Code Online (Sandbox Code Playgroud)

现在,我注意到如果这是用户输入,那么double.Parse可以抛出异常; 做以下事情可能是一个更好的主意:

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();
Run Code Online (Sandbox Code Playgroud)

现在如果用户输入错误,你不会得到异常; 你得到零.

  • @MoneyOrientedProgrammer:没有什么问题;这是正确的。`null` double 表示“我不知道该值是什么”。零加一加上你不知道的东西等于多少? (2认同)
  • @MoneyOrientedProgrammer:如果您的意图是“空值为零”,则使用“x??0”,其中“x”可为空。 (2认同)

Tho*_*que 19

我前段时间有类似的要求.基本上,我需要进行聚合,但我还需要选择每个中间值.所以我编写了一个名为SelectAggregate(可能不是最合适的名称,但我找不到更好的名称)的扩展方法,可以像这样使用:

double[] numbers = new [] { 0.3, 0.4, 0.3 };
double[] cumulativeSums = numbers.SelectAggregate(0.0, (acc, x) => acc + x).ToArray();
Run Code Online (Sandbox Code Playgroud)

这是代码:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


LBu*_*kin 5

您希望使用Aggregate运算符,使用a List<double>作为聚合累加器.这样你就可以产生一个本身就是一系列总和的投影.

这是一个让你入门的例子:

double[] runningTotal = textBox_f.Text
            .Split(new char[]{','})
            .Select(s => double.Parse(s))
            .Aggregate((IEnumerable<double>)new List<double>(), 
                       (a,i) => a.Concat(new[]{a.LastOrDefault() + i}))
            .ToArray();
Run Code Online (Sandbox Code Playgroud)


Bli*_*ndy 3

var input=new double[]{ ... }
double sum=0;

var output=input
    .Select(w=>sum+=w);
Run Code Online (Sandbox Code Playgroud)

  • 仅仅因为其副作用而使用 LINQ 查询是一个坏主意,原因有很多,其中最重要的是代码的未来读者不会想到这一点……它违反了最小意外原则。 (13认同)
  • @Blindy:我希望 Haskell 程序员会对你所做的事情感到非常震惊。Haskell 致力于避免此类副作用和突变。 (13认同)
  • 这是一个非常非常糟糕的主意。LBushkin 是对的。*请不要编写有副作用的查询*。查询应该“查询”数据,而不是“修改”数据。 (9认同)
  • @Blindy:你能解释一下 Select 与 map 有何不同吗?(变量的突变作为查询理解的副作用如何“起作用”?) (7认同)
  • 作为一个具体的例子来说明为什么这是一个坏主意,事实上`var l1 = output.ToList(); var l2 = output.ToList();` 具有不同内容的列表的结果可能会非常令人惊讶。 (3认同)
  • 哇,很短……而且很老套。请不要让我维护您的代码! (2认同)