6

シーケンス内の要素を検索し、2 つIEnumerable<T>の を返す次の拡張メソッドがあります。1 つはその要素の前のすべての要素を含み、もう 1 つは要素とそれに続くすべての要素を含みます。メソッドが遅延していればいいのですが、それを行う方法がわかりません。誰でも解決策を思い付くことができますか?

public static PartitionTuple<T> Partition<T>(this IEnumerable<T> sequence, Func<T, bool> partition)
{
    var a = sequence.ToArray();
    return new PartitionTuple<T>
    {
        Before = a.TakeWhile(v => !partition(v)),
        After = a.SkipWhile(v => !partition(v))
    };
}

すぐsequence.ToArray()に実行すると、怠惰の要件が無効になります。ただし、その行がないと、コストのかかるsequence反復が 2 回反復される可能性があります。そして、呼び出しコードが何をするかに応じて、さらに何度も。

4

4 に答える 4

4

オブジェクトを使用してLazy、2 つのパーティションのいずれかが繰り返されるまでソース シーケンスが配列に変換されないようにすることができます。

public static PartitionTuple<T> Partition<T>(
    this IEnumerable<T> sequence, Func<T, bool> partition)
{
    var lazy = new Lazy<IEnumerable<T>>(() => sequence.ToArray());
    return new PartitionTuple<T>
    {
        Before = lazy.MapLazySequence(s => s.TakeWhile(v => !partition(v))),
        After = lazy.MapLazySequence(s => s.SkipWhile(v => !partition(v)))
    };
}

このメソッドを使用して、シーケンス自体が繰り返されるまで遅延の評価を延期します。

public static IEnumerable<TResult> MapLazySequence<TSource, TResult>(
    this Lazy<IEnumerable<TSource>> lazy, 
    Func<IEnumerable<TSource>, IEnumerable<TResult>> filter)
{
    foreach (var item in filter(lazy.Value))
        yield return item;
}
于 2013-11-14T16:30:14.447 に答える
1

IEnumerable<T>これは、全体を強制的に反復することなく、1 回だけ反復されるようにany をメモ化する一般的なソリューションです。

public class MemoizedEnumerable<T> : IEnumerable<T>, IDisposable
{
   private readonly IEnumerator<T> _childEnumerator;
   private readonly List<T> _itemCache = new List<T>();

   public MemoizedEnumerable(IEnumerable<T> enumerableToMemoize)
   {
       _childEnumerator = enumerableToMemoize.GetEnumerator();
   }

   public IEnumerator<T> GetEnumerator()
   {
       return _itemCache.Concat(EnumerateOnce()).GetEnumerator();
   }

   public void Dispose()
   {
       _childEnumerator.Dispose();
   }

   private IEnumerable<T> EnumerateOnce()
   {
       while (_childEnumerator.MoveNext())
       {
           _itemCache.Add(_childEnumerator.Current);
           yield return _childEnumerator.Current;
       }
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
       return GetEnumerator();
   }
}

public static class EnumerableExtensions
{
    public static IEnumerable<T> Memoize<T>(this IEnumerable<T> enumerable)
    {
        return new MemoizedEnumerable<T>(enumerable);
    }
}

パーティショニングの問題に使用するには、次のようにします。

var memoized = sequence.Memoize();
return new PartitionTuple<T>
{
    Before = memoized.TakeWhile(v => !partition(v)),
    After = memoized.SkipWhile(v => !partition(v))
};

これはsequence、最大 1 回だけ繰り返されます。

于 2013-11-14T16:36:50.100 に答える
0

通常、カスタム クラスのオブジェクトを返すだけでIEnumerable<T>、列挙の要求に応じて結果を実装するだけでなく提供します。

IQueryable<T>の代わりに実装 (IEnumerable を継承)することもできますが、最終的な列挙要求でのみ実行されるデータベース クエリIEnumerable<T>を提供するようなクエリを使用してリーチ機能を構築するために必要です。linq for sql

于 2013-11-14T16:25:51.613 に答える