LINQ合并列表<;IEnumerable<;T>>;转换为一个IEnumerable<;T>;
本文关键字:gt lt IEnumerable 一个 LINQ 合并 转换 列表 | 更新日期: 2023-09-27 18:21:15
假设我有一个List<IEnumerable<double>>
,包含可变数量的无穷多个双数源。假设它们都是波发生器函数,我需要将它们叠加到一个由IEnumerable<double>
表示的单个波发生器中,只需从每个函数中取下一个数字并求和即可。
我知道我可以通过迭代器方法来做到这一点,比如:
public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
{
var funcs = from wfunc in wfuncs
select wfunc.GetEnumerator();
while(true)
{
yield return funcs.Sum(s => s.Current);
foreach (var i in funcs) i.MoveNext();
}
}
然而,它看起来相当"平淡无奇"。有没有一种LINQ式的方法可以实现这一点?
您可以在IEnumerables上聚合Zip方法。
public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
{
return wfuncs.Aggregate((func, next) => func.Zip(next, (d, dnext) => d + dnext));
}
这基本上是一遍又一遍地应用相同的Zip方法。有了四个IEnumerables,这将扩展到:
wfuncs[0].Zip(wfuncs[1], (d, dnext) => d + dnext)
.Zip(wfuncs[2], (d, dnext) => d + dnext)
.Zip(wfuncs[3], (d, dnext) => d + dnext);
试试看:fiddle
我想如果不扩展LINQ,就无法解决这个问题。这是我最后写的。我会尝试联系MoreLinq的作者,以某种方式将其包括在内,这在一些关键场景中可能很有用:
public static class EvenMoreLinq
{
/// <summary>
/// Combines mulitiple sequences of elements into a single sequence,
/// by first pivoting all n-th elements across sequences
/// into a new sequence then applying resultSelector to collapse it
/// into a single value and then collecting all those
/// results into a final sequence.
/// NOTE: The length of the resulting sequence is the length of the
/// shortest source sequence.
/// Example (with sum result selector):
/// S1 S2 S2 | ResultSeq
/// 1 2 3 | 6
/// 5 6 7 | 18
/// 10 20 30 | 60
/// 6 - 7 | -
/// - - |
/// </summary>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TResult">Result type</typeparam>
/// <param name="source">A sequence of sequences to be multi-ziped</param>
/// <param name="resultSelector">function to compress a projected n-th column across sequences into a single result value</param>
/// <returns>A sequence of results returned by resultSelector</returns>
public static IEnumerable<TResult> MultiZip<TSource, TResult>
this IEnumerable<IEnumerable<TSource>> source,
Func<IEnumerable<TSource>, TResult> resultSelector)
{
if (source == null) throw new ArgumentNullException("source");
if (source.Any(s => s == null)) throw new ArgumentNullException("source", "One or more source elements are null");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
var iterators = source.Select(s => s.GetEnumerator()).ToArray();
try
{
while (iterators.All(e => e.MoveNext()))
yield return resultSelector(iterators.Select(e => e.Current));
}
finally
{
foreach (var i in iterators) i.Dispose();
}
}
}
使用这个,我设法压缩了我的组合生成器:
interface IWaveGenerator
{
IEnumerable<double> Generator(double timeSlice, double normalizationFactor = 1.0d);
}
[Export(typeof(IWaveGenerator))]
class CombinedWaveGenerator : IWaveGenerator
{
private List<IWaveGenerator> constituentWaves;
public IEnumerable<double> Generator(double timeSlice, double normalizationFactor = 1)
{
return constituentWaves.Select(wg => wg.Generator(timeSlice))
.MultiZip(t => t.Sum() * normalizationFactor);
}
// ...
}
在这种情况下,LINQ可能更难理解,也不会给您带来任何好处。你最好的办法就是修正你的样本方法。像这样的东西应该起作用:
public IEnumerable<double> Generator(IReadOnlyCollection<IEnumerable<double>> wfuncs)
{
var enumerators = wfuncs.Select(wfunc => wfunc.GetEnumerator())
.ToList();
while(enumerators.All(e => e.MoveNext()))
{
yield return enumerators.Sum(s => s.Current);
}
}
有一种非常简单的方法。
public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
{
return wfuncs.SelectMany(list => list);
}