简体   繁体   English

LINQ 合并列表<IEnumerable<T> &gt; 变成一个 IEnumerable<T> 根据某种规则

[英]LINQ merge List<IEnumerable<T>> into one IEnumerable<T> by some rule

Lets say I have a List<IEnumerable<double>> containing variable number of infinite sources of double numbers.假设我有一个List<IEnumerable<double>>包含可变数量的无限双数来源。 Lets say they are all wave generator functions and I need to superimpose them into a single wave generator represented by IEnumerable<double> simply by taking the next number out of each and suming them.假设它们都是波发生器函数,我需要将它们叠加到由IEnumerable<double>表示的单个波发生器中,只需从每个函数中取出下一个数字并将它们相加即可。

I know I can do this through iterator methods, something like this:我知道我可以通过迭代器方法来做到这一点,就像这样:

    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();
        }
    } 

however, it seems rather "pedestrian".然而,它似乎相当“行人”。 Is there a LINQ-ish way to achieve this?有没有一种 LINQ-ish 方式来实现这一目标?

You could aggregate the Zip-method over the IEnumerables.您可以在 IEnumerables 上聚合 Zip 方法。

    public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
    {
        return wfuncs.Aggregate((func, next) => func.Zip(next, (d, dnext) => d + dnext));
    }

What this does is bascically applies the same Zip-method over and over again.这样做基本上是一遍又一遍地应用相同的 Zip 方法。 With four IEnumerables this would expand to:使用四个 IEnumerables 这将扩展为:

wfuncs[0].Zip(wfuncs[1], (d, dnext) => d + dnext)
         .Zip(wfuncs[2], (d, dnext) => d + dnext)
         .Zip(wfuncs[3], (d, dnext) => d + dnext);

Try it out: fiddle试试看:小提琴

I guess there is no way around this without extending LINQ.我想如果不扩展 LINQ,就没有办法解决这个问题。 So here's what I wrote in the end.所以这是我最后写的。 I'll try to contact MoreLinq authors to get this included in some way, it can be useful in some pivoting scenarios:我将尝试联系 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();
        }
    }
}

using this I managed to compress my combined generator:使用这个我设法压缩我的组合生成器:

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);
    }
    // ...
}

This is a situation where LINQ would probably be more difficult to understand, and not buy you anything.在这种情况下,LINQ 可能更难理解,而且不会给你买任何东西。 Your best bet is to just fix your sample method.最好的办法是修复您的示例方法。 Something like this should work:这样的事情应该工作:

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);
    }
} 

There is a very simple way to do this.有一种非常简单的方法可以做到这一点。

public IEnumerable<double> Generator(List<IEnumerable<double>> wfuncs)
{
    return wfuncs.SelectMany(list => list);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM