2012-09-19 11 views
12

「条件付き郵便番号」を実行できるC#の関数は既にありますか?C#にはすでにConditional Zip関数がありますか?

I.e.

異なる長さの入力を許可し、小さなソースの列挙子をインクリメントするタイミングを決める述語を取ることで、大きなソースのすべての要素が見えるようにする機能はありますか?

わかりやすい例として、素数の列挙可能性と整数の列挙可能性(どちらも昇順にソートされている)があると仮定してください。以前の素数以来、素数とすべての整数を保持する新しい列挙型を生成したい。

{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

面白いですね、だけでなく、ニッチ十分に私はあなたが既製の実装を見つけることはないだろうということ。 – Jon

+0

箱からすぐ外には何もありません。 –

答えて

4

私のソリューション:

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

これは、両方の列挙は一度だけ列挙されることを保証します。

使用法:

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

これは有効であり、O(n)(n:src2.Count())のように思われる私の答えと比較して、より良いです。 –

+0

@EvrenKuzucuoglu '.ToArray();はO(n)演算であり、' .Clear() 'も同様です。 – Magnus

+0

私はこのアイディアとZipのソースをとり、解決策を作りました。これを答えにしました。 –

4

いいですね。私はあなたが準備した関数をネットで直接見つけることはできませんが、あなたが望む操作が数学の標準的なアクションであれば、私はそこにライブラリがあると確信しています。あなたがそれを自分でやりたければ、グループごとに使うことができます。この特定のシナリオでは:

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

これは簡単な解決策のようです。その中に2人のメンバーを持つアノニマス型の列挙型が作成されます。

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

編集: あなたは辞書にそれを変換することができ素数の仕事にこれを注文する必要があります。

+0

ありがとう、私はあなたがそれを行うことができるか分からなかった!しかし、私は一度だけ列挙するZipのようなものが必要です。列挙型が複数回列挙されることは保証できません。 –

+1

実際、この場合のアルゴリズムはO(n * p)のようなものなので、本能的には最適ではないと言います。特定の最適なアルゴリズムを知っていても、標準ループで実装できない理由はありません。 –

0

これは私が(醜い実装)と一緒に行きましたが、一度だけenumerablesを列挙するものです。

/// <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素数=新しい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(); 
関連する問題