2011-01-28 26 views
13

Mam ciąg csv zawierający podwójne (np „0.3,0.4,0.3”) i chcę, aby być w stanie wyjścia podwójna tablica zawierająca skumulowaną sumę tych liczb (np. [0,3,0, 17,0]).użyciu LINQ znaleźć skumulowaną sumę tablicę liczb w C#

tej pory mam

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

co daje numery jako tablica, ale nie skumulowana suma liczb.

Czy istnieje jakiś sposób, aby kontynuować to wyrażenie, aby dostać to, co chcę, czy muszę używać iteracji, aby utworzyć nową tablicę z tablicy już mam?

+2

Lubię uczyć się nowych technologii i sposobów działania. Jest całkiem możliwe, że inne sposoby są lepsze lub szybsze, ale to jest coś, czego nie wiem, jak to zrobić, a więc chciałbym – simonalexander2005

+0

i dlaczego? Jeśli rozwiązanie bez LINQ jest zarówno szybsze do wpisania *, jak i * szybsze do wykonania, dlaczego powinieneś być zainteresowany rozwiązaniem LINQ? I dlaczego tak czy inaczej LINQ - dlaczego nie zapytać o rozwiązanie, które używa generycznych lub "dynamicznych", lub jakiejkolwiek innej losowej cechy, której nie potrzeba, aby odpowiedzieć na pytanie? – Timwi

+0

'Split (nowy char [] {','})' może być równoznacznie napisany 'Split (',')', ponieważ parametr jest zadeklarowany z 'params'. –

Odpowiedz

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

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

Zmodyfikowałem to na: prob = textBox_prob.Text.Split (new char [] {','}). Wybierz (s => double.Parse (s)). ToArray(). Wybierz (w => sum + = w) .ToArray(); bardziej zwięzły super, dzięki :) – simonalexander2005

+5

Używanie zapytań LINQ tylko dla ich efektów ubocznych jest złym pomysłem z wielu powodów - między innymi z tego, że przyszli czytelnicy kodu nie będą się tego spodziewać ... naruszają zasadę najmniej zaskakujące. – LBushkin

+0

takie proste i takie dobre. zdecydowanie lepiej niż moje. – Andrey

5

Chcesz użyć operatora Aggregate, z List<double> jako akumulatorem agregacji. W ten sposób możesz wytworzyć projekcję, która sama w sobie jest ciągiem sum.

Oto przykład, jak zacząć grę:

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

jest to nieefektywne, oblicza Suma za każdym razem, gdy jest O (n^2) – Andrey

+1

Nie kompiluje. Proszę przetestuj swój kod przed wysłaniem :( – Timwi

+0

Świetna odpowiedź, i dostał mnie patrząc na agregaty - ale w końcu wybrałem inny do użycia.Dziękuję choć – simonalexander2005

2

Dlaczego to musi być 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]); 
+0

Być może z tego samego powodu musi to być łączna suma, tablica liczb i C#? –

15

Miałem podobny wymóg jakiś czas temu. Zasadniczo potrzebowałem agregacji, ale musiałem również wybrać każdą wartość pośrednią. Więc napisałem metodę rozszerzenia nazwie SelectAggregate (prawdopodobnie nie jest to najbardziej odpowiednia nazwa, ale nie mogłem znaleźć nic lepszego następnie), które mogą być używane tak:

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

Oto kod:

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

+1 - dobre rozwiązanie, które uczy również, jak pisać kod wielokrotnego użytku! – Timwi

3

Przede wszystkim nie sądzę, że jest to dobre zadanie dla Linq. Zwykły stary foreach zrobi to lepiej. Ale jako układanka jest w porządku.

pierwsze celem było użyć podzapytania, ale nie podoba, ponieważ O (N^2). Oto moja liniowy rozwiązanie:

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

Oto sposób to zrobić przy użyciu 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(); 

W LINQPad:

  • 5,9
  • 12.9
40

Jest czas na ogólność i jest czas na rozwiązanie problemu. To jeden z ostatnich czasów. Jeśli chcesz stworzyć metodę, która zamieni sekwencję podwójnych w sekwencję sum częściowych, zrób to:

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

Łatwo. Bez problemów z agregatami i skomplikowanymi zapytaniami.Łatwe do zrozumienia, łatwe do debugowania, łatwy w użyciu:

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

Teraz zauważam, że jeśli to jest wejście użytkownik następnie double.Parse może rzucić wyjątek; lepszym pomysłem byłoby zrobienie czegoś takiego:

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

, a teraz nie otrzymasz wyjątku, jeśli użytkownik popełni błąd podczas pisania; otrzymujesz wartości null.

1

wykorzystanie RX:

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

Chociaż niezły kod, wymaga dodatkowego importu – simonalexander2005

Powiązane problemy