2012-09-19 10 views
12

Czy istnieje już funkcja w języku C#, która może wykonać "Zezwolenie warunkowe"?Czy istnieje już funkcja warunkowego Zip w języku C#?

tj.

Czy istnieje funkcja, która umożliwia wprowadzanie różnych długości i pobiera predykat określający, kiedy należy zwiększać moduł wyliczający mniejsze źródła, tak aby wszystkie elementy w większym źródle były widoczne?

Jako wymyślny przykład, załóżmy, że mamy przeliczalne liczby pierwsze i przeliczalne liczby całkowite (posortowane rosnąco). Chcemy stworzyć nowy przelicznik, który przechowuje liczbę główną i wszystkie liczby całkowite od czasu poprzedniego.

{2, 3, 5, 7, 11} 

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,} 

{2, [1]}, {3,[]}, {5, [4]}, {7, [6]}, {11, [8,9,10]} 
+6

Brzmi interesująco, ale także niszę na tyle, że wątpię znajdziesz gotową implementację. – Jon

+0

Nic po wyjęciu z pudełka. –

Odpowiedz

4

Moje rozwiązanie:

public static IEnumerable<Tuple<T1, IEnumerable<T2>>> ConditionalZip<T1, T2>(
    this IEnumerable<T1> src1, 
    IEnumerable<T2> src2, 
    Func<T1, T2, bool> check) 
{ 
    var list = new List<T2>(); 
    using(var enumerator = src2.GetEnumerator()) 
    { 
     foreach(var item1 in src1) 
     { 
      while(enumerator.MoveNext()) 
      { 
       var pickedItem = enumerator.Current; 
       if(check(item1, pickedItem)) 
       { 
        list.Add(pickedItem); 
       } 
       else 
       { 
        break; 
       } 
      } 
      var items = list.ToArray(); 
      list.Clear(); 
      yield return new Tuple<T1, IEnumerable<T2>>(item1, items); 
     } 
    } 
} 

To gwarantuje, że oba wyliczenia zostaną wymienione tylko raz.

Zastosowanie:

var src1 = new int[] { 2, 3, 5, 7, 11 }; 
var src2 = Enumerable.Range(1, 11); 
Func<int, int, bool> predicate = (i1, i2) => i1 > i2; 
var result = src1.ConditionalZip(src2, predicate); 
+0

Jest to poprawne i porównane z moją odpowiedzią, która wygląda jak O (n) (n: src2.Count()), więc lepiej. –

+0

@EvrenKuzucuoglu Chociaż '.ToArray();' jest operacją O (n) i tak samo jest '.Clear()'. – Magnus

+0

Wziąłem tę ideę i źródło Zip i zrobiłem rozwiązanie. Oznaczono to jako odpowiedź. –

4

To miło. Nie sądzę, abyś znalazł gotową funkcję bezpośrednio w .net, ale jeśli wymagana operacja jest standardową akcją w matematyce, to jestem pewien, że jest gdzieś biblioteka, która to robi. Jeśli chcesz zrobić to samemu, możesz użyć grupy według. W tym szczególnym scenariuszu:

var primes = new List<int> {2, 3, 5, 7, 11}; 
var numbers = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 

var groups = 
    from number in numbers 
    group number by primes.First(prime => prime >= number) into gnumber 
    select new { 
     prime = gnumber.Key, 
     numbers = gnumber.Where(n => n != gnumber.Key) 
    }; 

To wydaje się dość proste rozwiązanie. Stworzy to przelicznik typu anonoimous z dwoma członkami. można przekształcić go w słowniku:

var dict = groups.ToDictionary(g => g.prime, g=> g.numbers); 

Edit: liczby pierwsze, musi być zamawiane dla tej pracy.

+0

Dzięki, nie wiedziałem, że możesz to zrobić! Jednak potrzebuję czegoś takiego jak Zip, który wylicza tylko raz. Nie mogę zagwarantować, że wyliczenia można wyliczyć więcej niż jeden raz. –

+1

Rzeczywiście w tym przypadku algorytm jest podobny do O (n * p), więc instynktownie powiedziałbym, że nie jest optymalny. Jeśli znasz konkretny optymalny algorytm, nie ma powodu, dla którego nie byłbyś w stanie go wdrożyć za pomocą standardowych pętli. –

0

To właśnie pojechałem z (brzydki realizacja), ale wylicza enumerables tylko raz.

/// <summary> 
    /// Merges two sequences by using the specified predicate function to determine when to iterate the second enumerbale. 
    /// </summary> 
    /// 
    /// <returns> 
    /// An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains merged elements of two input sequences. 
    /// </returns> 
    /// <param name="larger">The first sequence to merge.</param><param name="smaller">The second sequence to merge.</param> 
    /// <param name="resultSelector">A function that specifies how to merge the elements from the two sequences (a flag is passed into the dunction to notify when elements of the second source are exhausted.</param> 
    /// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam> 
    /// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam> 
    /// <typeparam name="TResult">The type of the elements of the result sequence.</typeparam> 
    public static IEnumerable<TResult> ConditionalZip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> larger, IEnumerable<TSecond> smaller, Func<TFirst, TSecond, bool> predicate, Func<TFirst, TSecond, bool, TResult> resultSelector) 
    { 
     if (larger == null) 
      throw new ArgumentNullException("larger"); 
     if (smaller == null) 
      throw new ArgumentNullException("smaller"); 
     if (resultSelector == null) 
      throw new ArgumentNullException("resultSelector"); 
     else 
      return ConditionalZipIterator(larger, smaller, predicate, resultSelector); 
    } 

    private static IEnumerable<TResult> ConditionalZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, bool> predicate, Func<TFirst, TSecond, bool, TResult> resultSelector) 
    { 
     using (IEnumerator<TFirst> enumerator1 = first.GetEnumerator()) 
     { 
      using (IEnumerator<TSecond> enumerator2 = second.GetEnumerator()) 
      { 
       if (!enumerator2.MoveNext()) 
       { 
        secondIsFinished = true; 
       } 
       currentSecond = secondIsFinished ? default(TSecond) : enumerator2.Current; 

       while (enumerator1.MoveNext()) 
       { 

        while (!secondIsFinished && !predicate(enumerator1.Current, currentSecond)) 
        { 
         if (!enumerator2.MoveNext()) 
         { 
          secondIsFinished = true; 
         } 
         currentSecond = secondIsFinished ? default(TSecond) : enumerator2.Current; 
        } 


        yield return resultSelector(enumerator1.Current, currentSecond, secondIsFinished); 
       } 
      } 
     } 
    } 

usuage

var bodźce = new int [] {2, 3, 5, 7, 11} .ThrowIfEnumeratedMoreThan (1); var ints = Enumerable.Range (1, 20) .ThrowIfEnumeratedMoreThan (1);

 var results = ints.ConditionalZip(primes, (i, prime) => i <= prime, (i, prime, isEmpty) => new {i, prime, wasMatched=!isEmpty}) 
      .Where(x => x.wasMatched) 
      .GroupBy(x => x.prime) 
      .Select(x => new {Prime = x.Key, Values = x.Where(n => n.i != n.prime).Select(n=>n.i).ToArray()}) 
      .ToArray(); 
Powiązane problemy