22

double を含む csv 文字列 (例: "0.3,0.4,0.3") があり、これらの数値の累積合計 (例: [0.3,0.7,1.0]) を含む double 配列を出力できるようにしたいと考えています。

これまでのところ、私は

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

これは数値を配列として提供しますが、数値の累積合計ではありません。

この式を続けて必要なものを取得する方法はありますか、または反復を使用して、既に持っている配列から新しい配列を作成する必要がありますか?

4

11 に答える 11

53

一般化する時があり、実際に提起された問題を解決する時があります。これは後者の 1 つです。double のシーケンスを部分和のシーケンスに変換するメソッドを作成する場合は、次のようにします。

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 => double.Parse(s))
    .CumulativeSum()
    .ToArray();

ここで、それがユーザー入力である場合、 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();

ユーザーが入力ミスをしても例外が発生しなくなりました。あなたはヌルを取得します。

于 2011-01-28T18:47:04.867 に答える
21

私はしばらく前に同様の要件を持っていました。基本的には集計を行う必要がありましたが、各中間値も選択する必要がありました。そこで、次のように使用できる名前の拡張メソッドを作成しましたSelectAggregate(おそらく最も適切な名前ではありませんが、それ以上のものは見つかりませんでした)。

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

コードは次のとおりです。

    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;
        }
    }
于 2011-01-28T00:25:26.557 に答える
5

を集約アキュムレータとして使用して、Aggregate演算子を使用します。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();
于 2011-01-28T00:23:28.550 に答える
2

なぜLINQである必要があるのですか?

var cumulative = new double[probabilities.Length];
for (int i = 0; i < probabilities.Length; i++)
    cumulative[i] = probabilities[i] + (i == 0 ? 0 : cumulative[i-1]);
于 2011-01-28T00:23:52.947 に答える
2
var input=new double[]{ ... }
double sum=0;

var output=input
    .Select(w=>sum+=w);
于 2011-01-28T00:26:44.053 に答える
2

まず第一に、Linq にとって良い仕事だとは思いません。普通の古いforeach方がうまくいくでしょう。でもパズルとしてはいいです。

最初のアイデアはサブクエリを使用することでしたが、O(n^2) であるため、好きではありません。これが私の線形ソリューションです:

        double[] probabilities = new double[] { 0.3, 0.4, 0.3};
        probabilities
            .Aggregate(
                new {sum=Enumerable.Empty<double>(), last = 0.0d},
                (a, c) => new {
                    sum = a.sum.Concat(Enumerable.Repeat(a.last+c,1)),
                    last = a.last + c
                },
                a => a.sum
            );
于 2011-01-28T00:33:39.703 に答える
1

RX を使用:

var input=new double[]{ ... }
var output = new List<double>();
input.ToObservable().Scan((e, f) => f + e).Subscribe(output.Add);
于 2016-02-24T18:44:07.960 に答える
0

LINQ を使用してそれを行う方法は次のとおりです

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 で:

  • 4
  • 5.9
  • 10
  • 12.9
于 2011-01-28T00:38:23.343 に答える