2015-11-09 16 views
7

List<IEnumerable<double>>には可変数の無限の倍数の倍数のソースが含まれています。彼らはすべての波ジェネレータ関数であると言いましょう。私はIEnumerable<double>で表される単一の波発生器にそれらを重ねて、それぞれから次の数を取り出して合計する必要があります。LINQのマージリスト<IEnumerable <T>>をある規則で1つのIEnumerable <T>に変換します

私は反復子メソッドを介してこれを行うことができます知っている、このような何か:

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-ishの方法はありますか?

+3

'SelectManyを'、多分? –

+0

しかし、どのように?私はそれのまわりで私の頭を包み込むように努力しました。私が必要とするのは、可変数のシーケンスを持つZip()の変わった変種です。 – mmix

+2

なぜあなたはLINQでそれを望んでいますか?あなたの実装は人間が読めるし、かなり良いです。 –

答えて

4

私は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); 
    } 
    // ... 
} 
+1

メモリ内のリストまたは配列に 'iterators'の結果を確実にキャプチャしてください。さもなければ、 'MoveNext()'の呼び出しは '.Current'の呼び出しとは異なる列挙子オブジェクトで動作します:それらを反復処理するたびに' .Select() 'を再評価しています。 – StriplingWarrior

+0

真実は見落とされています。ありがとう。 – mmix

+2

'iterators'は決して' null'になることはありません。そのため、nullをチェックする点はありません。 – Servy

8

IEnumerablesでZipメソッドを集計できます。

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

これは基本的に同じZipメソッドを何度も繰り返し適用します。

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

はそれを試してみてください::4 IEnumerablesでは、これはに拡大するfiddle

+0

は私にとってかなり寂しいようです。建設的なアイデアのためには – Hristo

+0

+1。しかし、 'Zip'は関連する列挙型のいずれかの**が終了すると停止しますが、OPが望むのは** all **終了まで降伏し続けることです。 –

+0

@IvanStoev:OPは、IEnumerable <>が「無限」であると言っています。 – StriplingWarrior

3

これは、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); 
    } 
} 
+0

これは簡単で読みやすいと思います。あなたはメソッドの入力パラメータの型を変更しましたが、なぜIEnumerable >を使用しませんでしたか?あなたは "外側の" 'IEnumerable <>'も無限になるだろうと恐れていますか? –

+0

@JeppeStigNielsen:ほとんどの場合、 'IReadOnlyCollection <>'を使うのと同じです。私は、複数の列挙問題を持つ呼び出しコードの可能性を減らすことが分かりました。しかし、「IEnumerable >」は完全に有効です。特に、より一般的な目的にしようとしている場合は、mmixが答えてくれました。 – StriplingWarrior

関連する問題