是否有可能编写一个使IEnumerable
被多次使用而仅一次通过而又不将所有数据读入内存的高阶函数?
例如,在下面的代码中,可枚举是mynums
(为了方便枚举,我在上面标记了.Trace()
)。目的是确定是否有大于5的数字以及所有数字的总和。处理两次可枚举的函数为Both_TwoPass
,但将其枚举两次。相反,Both_NonStream
仅枚举一次,但以将其读入内存为代价。原则上,可以像Any5Sum
所示以一次通过和以流方式执行这两项任务,但这是特定的解决方案。是否可以编写一个具有与Both_*
相同签名的函数,但这是两全其美的方法?
(在我看来这应该可以使用线程。是否有更好的解决方案,例如使用async
?]
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp
{
static class Extensions
{
public static IEnumerable<T> Trace<T>(this IEnumerable<T> tt, string msg = "")
{
Console.Write(msg);
try
{
foreach (T t in tt)
{
Console.Write(" {0}", t);
yield return t;
}
}
finally
{
Console.WriteLine('.');
}
}
public static (S1, S2) Both_TwoPass<T, S1, S2>(this IEnumerable<T> tt, Func<IEnumerable<T>, S1> f1, Func<IEnumerable<T>, S2> f2)
{
return (f1(tt), f2(tt));
}
public static (S1, S2) Both_NonStream<T, S1, S2>(this IEnumerable<T> tt, Func<IEnumerable<T>, S1> f1, Func<IEnumerable<T>, S2> f2)
{
var tt2 = tt.ToList();
return (f1(tt2), f2(tt2));
}
public static (bool, int) Any5Sum(this IEnumerable<int> ii)
{
int sum = 0;
bool any5 = false;
foreach (int i in ii)
{
sum += i;
any5 |= i > 5; // or: if (!any5) any5 = i > 5;
}
return (any5, sum);
}
}
class Program
{
static void Main()
{
var mynums = Enumerable.Range(0, 10).Trace("mynums:");
Console.WriteLine("TwoPass: (any > 5, sum) = {0}", mynums.Both_TwoPass(tt => tt.Any(k => k > 5), tt => tt.Sum()));
Console.WriteLine("NonStream: (any > 5, sum) = {0}", mynums.Both_NonStream(tt => tt.Any(k => k > 5), tt => tt.Sum()));
Console.WriteLine("Manual: (any > 5, sum) = {0}", mynums.Any5Sum());
}
}
}
您绝对可以在foreach
中调用任意数量的操作。我不确定您要查找哪种语法,但是您似乎在询问基本嵌套的foreach
和操作列表:
public static void CallManyActions<T>(IEnumerable<T> tt,
params Action<T>[] actions)
{
foreach (var t in tt)
{
foreach (var a in actions)
{
a(t);
}
}
}
并称呼它
var mynums = Trace(Enumerable.Range(0, 10),"mynums:");
var sum = 0;
var has5 = false;
CallManyActions(mynums, (x => sum +=x), (i => has5 |= (i>5)));
如果您只使用LINQ,请查看Enumerable.Aggregate
调用中的多项操作。