仅在一个 LINQ 表达式中获取 IEnumerable 集合的总和
本文关键字:IEnumerable 获取 集合 表达式 LINQ 一个 | 更新日期: 2023-09-27 17:56:51
假设我有一个 inifite 生成器A()
.我想要的是获取A
返回的所有数字的总和,以使总和不超过仅在一个 LINQ 表达式中N
的值。
我想知道是否有一种扩展方法可以帮助我?
经典的方法是:
int sum = 0;
foreach (int x in A()) {
sum += x;
if (sum > N) {
break;
}
}
return sum;
但我一直在思考如何只用一种表达方式做到这一点,但没有成功......
使用标准的惯用 LINQ,这是不可能的。 您需要的语义是Aggregate()
和TakeWhile()
的组合。 否则,您需要产生副作用,这在 LINQ 中是禁忌。
以下是一种副作用方法的示例:
var temp = 0;
var sum = A().TakeWhile(i =>
{
var res = !(temp > N);
temp += i;
return res;
}).Sum();
如果A
是无限生成器,则无法仅使用内置 LINQ 方法在单个语句中执行此操作。
为了在单个语句中干净利落地做到这一点,没有副作用,您可能需要使用某种Scan
方法来计算输入序列的前缀和。然后你只需要第一个大于 N
的元素。容易!
int sum = A().Scan((s, x) => s + x).First(s => s > N);
// ...
public static class EnumerableExtensions
{
public static IEnumerable<T> Scan<T>(
this IEnumerable<T> source, Func<T, T, T> func)
{
if (source == null) throw new ArgumentNullException("source");
if (func == null) throw new ArgumentNullException("func");
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
T accumulator = e.Current;
yield return accumulator;
while (e.MoveNext())
{
accumulator = func(accumulator, e.Current);
yield return accumulator;
}
}
}
}
}
当然,有一种方法可以使用单个 LINQ 表达式来执行此操作。我能想到的最简单的,仍然有一些普遍性和优雅
性是:public static int SumWhile(this IEnumerable<int> collection, Func<int, bool> condition)
{
int sum = 0;
foreach (int i in collection)
{
sum += i;
if (!condition(sum))
break;
}
return sum;
}
可以这样称为:
int sum = A().SumWhile(i => i <= N);
是的,只是一个 LINQ 表达式!玩得开心
可能是最接近你最初的想法:
int sum = 0;
int limit = 500;
A().TakeWhile(i => (sum += i) < limit).Count();
//Now the variable named sum contains the smaller sum of elements being >= limit
Count() 不用于其返回值,而是用于强制实际枚举。
让我们看看我的要求是否正确。
A() 是一个无限生成器。根据定义,它会永远生成值(在本例中为整数)。
您希望查找所有小于 N 的值,并将它们相加。
Linq不是问题所在。在 A() 完成生成之前,您不会完成添加...而这永远不会发生。
顺便说一句,你发布的代码并没有把所有小于N的值都加起来......它会把所有值加起来,直到找到一个小于N的值,然后它就停止寻找了。这是你的意思吗?
我相信
下面的可怕代码可以满足您的要求。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApplication12 {
public class Program {
public static void Main(string[] args) {
const int N=100;
int sum;
try {
sum=A().Aggregate((self, next) => {
if(self+next<=N)
return self+next;
else
throw new ResultException(self);
});
} catch(ResultException re) {
sum=re.Value;
}
Debug.Print("Sum="+sum);
}
private class ResultException : Exception {
public readonly int Value;
public ResultException(int value) {
Value=value;
}
}
private static IEnumerable<int> A() {
var i=0;
while(true) {
yield return i++;
}
}
}
}
int
sum = A()。其中(x => x <N)。总和();>