Ответ 1
var input=new double[]{ ... }
double sum=0;
var output=input
.Select(w=>sum+=w);
У меня есть строка csv, содержащая удвоения (например, "0,3, 0,0,3" ), и я хочу иметь возможность выводить двойной массив, содержащий кумулятивную сумму этих чисел (например, [0,3,0,7,1,0]).
До сих пор у меня
double[] probabilities = textBox_f.Text.Split(new char[]{','}).Select(s => double.Parse(s)).ToArray();
который дает числа в виде массива, но не кумулятивную сумму чисел.
Есть ли способ продолжить это выражение, чтобы получить то, что я хочу, или мне нужно использовать итерацию для создания нового массива из массива, который у меня уже есть?
var input=new double[]{ ... }
double sum=0;
var output=input
.Select(w=>sum+=w);
Там время для общности, и есть время для решения фактически поставленной проблемы. Это один из последних времен. Если вы хотите создать метод, который превращает последовательность удвоений в последовательность частичных сумм, просто выполните это:
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();
и теперь вы не получите исключения, если пользователь допустил ошибку ввода; вы получаете нули.
У меня было подобное требование некоторое время назад. В принципе, мне нужно было выполнить агрегацию, но мне также нужно было выбрать каждое промежуточное значение. Поэтому я написал метод расширения с именем 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;
}
}
Вы хотите использовать оператор 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();
Прежде всего, я не думаю, что это хорошая задача для 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
);
Зачем нужно 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]);
Здесь a способ использования 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:
использовать RX:
var input=new double[]{ ... }
var output = new List<double>();
input.ToObservable().Scan((e, f) => f + e).Subscribe(output.Add);