2015-11-09 17 views
7

Lets sagen, ich habe eine List<IEnumerable<double>> enthält variable Anzahl von unendlichen Quellen von doppelten Zahlen. Nehmen wir an, sie sind alle Wellengeneratorfunktionen und ich muss sie in einen einzelnen Wellengenerator überlagern, der durch IEnumerable<double> repräsentiert wird, indem einfach die nächste Nummer aus jedem genommen wird.LINQ Zusammenführen Liste <IEnumerable <T>> in einem IEnumerable <T> durch eine Regel

Ich weiß, dass ich diese Methoden durch Iterator tun können, etwa wie folgt:

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

aber es scheint eher „Fußgänger“. Gibt es einen LINQ-ischen Weg, dies zu erreichen?

+3

'Select ', vielleicht? –

+0

Aber wie? Ich versuchte, meinen Kopf darum zu wickeln, ohne Glück. Was ich brauche ist eine seltsame Variante von Zip() mit variabler Anzahl von Sequenzen. – mmix

+2

nur wundernd. Warum würdest du es in LINQ wollen? Ihre Implementierung ist menschlich lesbar und ziemlich nett. –

Antwort

4

Ich denke, es gibt keinen Weg um diese zu erweitern, ohne LINQ zu erweitern. Also hier ist, was ich am Ende geschrieben habe. Ich werde versuchen, MoreLinq Autoren zu kontaktieren dies in irgendeiner Weise einbezogen zu bekommen, kann es in einigen Schwenk Szenarien nützlich sein:

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

mit diesem ich meine kombinierten Generator komprimieren verwaltet:

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

Stellen Sie sicher, dass das Ergebnis von Iteratoren in einer speicherinternen Liste oder einem Array erfasst wird. Andernfalls wird der Aufruf von 'MoveNext()' an einem anderen Enumerator-Objekt als dem Aufruf von '.Current' arbeiten: Sie werten '.Select()' jedes Mal neu aus, wenn Sie über sie iterieren. – StriplingWarrior

+0

Wahr, übersehen, dass. Vielen Dank. – mmix

+2

'iterators' können niemals' null' sein, daher ist es sinnlos, sie zu prüfen. – Servy

8

Sie könnten die Zip-Methode über die IEnumerables aggregieren.

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

Was dies tut, gilt grundsätzlich die gleiche Zip-Methode immer und immer wieder. Mit vier IEnumerables würden diese erweitern:

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

Ausprobieren: fiddle

+0

scheint mir ziemlich fremd zu sein. – Hristo

+0

+1 für die konstruktive Idee. Jedoch wird 'Zip' gestoppt, wenn ** any ** der beteiligten Enumerables endet, während das OP will, dass es nachgibt, bis ** all ** endet. –

+0

@IvanStoev: OP sagt, dass die 'IEnumerable <>' s sind "unendlich", was ich zu bedeuten, keiner von ihnen wird jemals aufhören, Werte zu produzieren. – StriplingWarrior

3

Dies ist eine Situation, in der LINQ wahrscheinlich schwieriger zu verstehen wäre, und Sie nichts kaufen. Ihre beste Wette ist, nur Ihre Beispielmethode zu reparieren. So etwas sollte funktionieren:

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

Ich denke, das ist einfach und leicht zu lesen. Sinus Sie haben den Typ des Methodeneingabeparameters geändert, warum haben Sie nicht einfach IEnumerable > '? Haben Sie Angst, dass das "äußere" IEnumerable <> auch unendlich sein wird? –

+0

@JeppeStigNielsen: Meistens verwende ich nur 'IReadOnlyCollection <>' s. Ich finde, dass sie die Wahrscheinlichkeit verringern, dass der aufrufende Code mehrere Aufzählungsprobleme hat. Aber "IEnumerable >" wäre perfekt gültig, besonders wenn du versuchst, es allgemeiner zu machen, wie es in seiner Antwort mixte. – StriplingWarrior

Verwandte Themen