Frage

Ich habe eine CSV -Zeichenfolge, die Doppel enthält (z. "0,3,0,4,0,3"), und ich möchte in der Lage sein, ein Doppelarray auszugeben, das die kumulative Summe dieser Zahlen enthält (z. B. [0,3,0,7,1,0]).

Bisher habe ich

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

Das gibt die Zahlen als Array, aber nicht die kumulative Summe der Zahlen.

Gibt es eine Möglichkeit, diesen Ausdruck fortzusetzen, um das zu bekommen, was ich will, oder muss ich eine Iteration verwenden, um ein neues Array aus dem Array zu erstellen, das ich bereits habe?

War es hilfreich?

Lösung

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

var output=input
    .Select(w=>sum+=w);

Andere Tipps

Es gibt eine Zeit für Allgemeinheit und es gibt eine Zeit, um das Problem zu lösen, das tatsächlich dargestellt wird. Dies ist eine der letzten Zeiten. Wenn Sie eine Methode erstellen möchten, die eine Abfolge von Doppel in eine Sequenz von Teilsummen verwandelt, dann tun Sie einfach:

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

Einfach. Kein Durcheinander mit Aggregaten und komplizierten Fragen und so weiter. Leicht zu verstehen, leicht zu debuggen, einfach zu bedienen:

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

Jetzt stelle ich fest, dass, wenn dies Benutzereingabe ist, doppelt.Parse eine Ausnahme ausgeben kann. Es könnte eine bessere Idee sein, so etwas wie zu tun:

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();

Und jetzt erhalten Sie keine Ausnahme, wenn der Benutzer einen Schreibfehler macht. Du bekommst Nulls.

Ich hatte vor einiger Zeit eine ähnliche Anforderung. Grundsätzlich musste ich eine Aggregation durchführen, aber ich musste auch jeden Zwischenwert auswählen. Also schrieb ich eine Erweiterungsmethode namens namens SelectAggregate (Wahrscheinlich nicht der am besten geeignete Name, aber ich konnte dann nichts Besseres finden), das so verwendet werden kann:

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

Hier ist der Code:

    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;
        }
    }

Sie möchten die verwenden Aggregate Bediener mit a List<double> als Aggregationsakkumulator. Auf diese Weise können Sie eine Projektion erzeugen, die selbst eine Folge von Summen ist.

Hier ist ein Beispiel, um Ihnen den Einstieg zu erleichtern:

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();

Warum muss es Linq sein?

var cumulative = new double[probabilities.Length];
for (int i = 0; i < probabilities.Length; i++)
    cumulative[i] = probabilities[i] + (i == 0 ? 0 : cumulative[i-1]);

Zunächst denke ich nicht, dass es für Linq eine gute Aufgabe ist. Schlicht alt foreach wird es besser machen. Aber als Puzzle ist es in Ordnung.

Die erste Idee war, Unterabfragen zu verwenden, aber ich mag es nicht, weil es o (n^2) ist. Hier ist meine lineare Lösung:

        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
            );

Verwenden Sie RX:

var input=new double[]{ ... }
var output = new List<double>();
input.ToObservable().Scan((e, f) => f + e).Subscribe(output.Add);

Dies ist eigentlich ziemlich unkompliziert, um mit dem Generator zu verallgemeinern. Hier ist eine neue Erweiterungsmethode genannt Accumulate das funktioniert wie eine Kombination von Select und Aggregate. Es gibt eine neue Sequenz zurück, indem eine Binärfunktion auf jedes Element in der Sequenz angewendet wird und der Wert bisher akkumuliert wird.

 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);
    }
 }

Hier ist a Möglichkeit, es mit LINQ zu tun:

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();

In linqpad:

  • 4
  • 5.9
  • 10
  • 12.9

Kumulative Summe für List<double>:

var nums = new List<double>() { 0.3, 0.0, 0.4, 1.1 };
var cumsum = nums.Aggregate(new List<double> (), 
              (list, next) => { list.Add(list.LastOrDefault() + next); return list; });
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top