С# Linq агрегирует промежуточные значения
Учитывая массив положительных и отрицательных чисел, есть выражение Linq, которое может получать промежуточные значения или максимальное значение?
например
var heights = new List<int>();
var numbers = new [] { 5, 15, -5, -15 };
var curHeight = 0;
foreach (var number in numbers)
{
curHeight = curHeight + number;
heights.add(curHeight);
}
Эта функция вернет [5, 20, 15, 0]
Агрегат можно использовать одинаково, и он будет проходить через эту последовательность
numbers.aggregate((a, b) => a + b);
0 + 5 = 5, 5 + 15 = 20, 20 - 5 = 15, 15 - 15 = 0
Мой вопрос в том, есть ли способ использовать агрегат или какой-либо другой, чтобы возвращались промежуточные значения [5, 20, 15, 0]
?
В качестве альтернативы можно ли отслеживать максимальное значение? Мне действительно нужно вернуть значение 20.
Ответы
Ответ 1
Вам нужна настраиваемая версия агрегата:
public static IEnumerable<R> AggregateSequence<A, R>(
this IEnumerable<A> items,
Func<A, R, R> aggregator,
R initial)
{
// Error cases go here.
R result = initial;
foreach(A item in items)
{
result = aggregator(item, result);
yield return result;
}
}
Это общий механизм для решения вашей конкретной проблемы:
public static IEnumerable<int> MovingSum(this IEnumerable<int> items)
{
return items.AggregateSequence( (item, sum) => item + sum, 0 );
}
И теперь вы можете решить свою проблему с помощью
mySequence.MovingSum().Max();
Ответ 2
С использованием Aggregate
:
var result = numbers.Aggregate(new List<int>(), (a,b)=>
{
a.Add(a.LastOrDefault()+b);
return a;
});
результат wil be:
[5,20,15,0]
Если вы хотите иметь значение Max, вы можете использовать собственный класс результата:
public class Result
{
public List<int> Values {get;set;}
public int Max => Values.Max();
public Result()
{
Values = new List<int>();
}
}
используйте его:
var result = numbers.Aggregate(new Result(), (a,b)=>
{
a.Values.Add(a.Values.LastOrDefault()+b);
return a;
});
и вы получите тот же список, что и result.Values
и [20], как result.Max
Ответ 3
Вы можете сделать это в своей совокупной функции:
var intermidiateValues = new List<int>();
numbers.aggregate((intermidiateValue, next) => {
intermidiateValues.Add(intermidiateValue);
return intermidiateValue + next;
});
И затем используйте
intermidiateValues.Max();
Ответ 4
Решение с BAD-производительностью:
static void Main(string[] args)
{
var numbers = new[] { 5, 15, -5, -15 };
var heights = numbers.Select((o, i) => numbers.Take(i + 1).Sum()).ToList();
foreach (var item in heights)
{
Console.WriteLine(item);
}
}
Он имеет сложность O (n ^ 2).
Ответ 5
Вы можете написать свои собственные методы расширения для выполнения итоговых сумм накопления/выполнения с помощью настраиваемой проекции и называть его следующим:
static void Main(string[] args) {
var numbers = new[] { 5, 15, -5, -15 };
// results 5, 20, 15, 0
var results = numbers.Accumulate((a, b) => a + b);
var max = results.Max(); // 20
// providing a initial seed value: 20, 35, 30, 15:
var result2 = numbers.Accumulate(15, (a, b) => a + b);
var max2 = result2.Max(); // 35
}
Методы расширения:
public static class AccumulateExt
{
public static IEnumerable<TSource> Accumulate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> funcAggregate)
{
return source.Accumulate(default(TSource), funcAggregate);
}
public static IEnumerable<TAggregate> Accumulate<TSource, TAggregate>(
this IEnumerable<TSource> source,
TAggregate initialValue,
Func<TAggregate, TSource, TAggregate> funcAggregate)
{
return AccumulateImplementation(source, initialValue, funcAggregate, (_, agg) => agg);
}
public static IEnumerable<TResult> Accumulate<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> funcAggregate,
Func<TSource, TSource, TResult> resultSelector)
{
return source.Accumulate(default(TSource), funcAggregate, resultSelector);
}
public static IEnumerable<TResult> Accumulate<TSource, TAggregate, TResult>(
this IEnumerable<TSource> source,
TAggregate initialValue,
Func<TAggregate, TSource, TAggregate> funcAggregate,
Func<TSource, TAggregate, TResult> resultSelector)
{
return AccumulateImplementation(source, initialValue, funcAggregate, resultSelector);
}
private static IEnumerable<TResult> AccumulateImplementation<TSource, TAggregate, TResult>(
this IEnumerable<TSource> source,
TAggregate initialValue,
Func<TAggregate, TSource, TAggregate> funcAggregate,
Func<TSource, TAggregate, TResult> resultSelector)
{
var last = initialValue;
foreach (var item in source)
{
var value = funcAggregate(last, item);
last = value;
yield return resultSelector(item, value);
}
}
}
Ответ 6
версия с одной строкой
var numbers = new[] { 5, 15, -5, -15 };
var curHeight = 0;
int best = numbers.Select(x => { curHeight += x; return curHeight; }).Max();
как отметил Эрик, это правильно сказано, что плохая практика, хотя она работает здесь, будет не разрушительным способом ее выполнения.
var numbers = new[] { 5, 15, -5, -15 };
int best = Enumerable.Range(1, numbers.Length + 1).Select(x => numbers.Take(x).Sum()).Max();