2011-06-28 24 views
9

私は訪問者タイプのパターンを使用してコレクションを反復しており、リスト内の現在および次のアイテムにアクセスする必要があります。現時点では私はこれを達成するための他の/より良い/よりエレガントな方法があるかどうかと思いまして、このLinq - ルックアヘッド反復

public void Visit<TItem>(this IEnumerable<TItem> theList, Action<TItem, TItem> visitor) 
{ 
    for (i = 0; i <= theList.Count - 1; i++) { 
     if (i == theList.Count - 1) { 
      visitor(theList(i), null); 
     } else { 
      visitor(theList(i), theList(i + 1)); 
     }  
    }  
} 

のような拡張メソッドを経由して、それをやっていますか?現時点では、リスト内の現在のアイテムと次のアイテムにアクセスする必要があると思いますが、次の「n」アイテムを先読みする必要があるかもしれない状況に遭遇するかどうかは疑問です。

+1

あなたのコードはコンパイルされません。 'IEnumerable 'には 'Count'という名前のプロパティはありません(メソッドグループがあります)。 – jason

+0

@Jason - thx - VBでオリジナルを書いています(これはIEnumerableのCountをサポートしているようです)! Thx –

答えて

9

は、.NET 4を使用していると仮定すると、あなたが同じことを達成するためにZipを使用することができます。

var query = original.Zip(original.Skip(1), 
         (current, next) => new { current, next }); 

この意志シーケンスを超える反復二度けれども。 IEnumerableはCountプロパティを持たないので、あなたの現在の拡張メソッド(私は動作しないと思っていますが、これはbtwという便利な方法です)と同様にメソッドtheListを呼び出そうとしています...)以下のように:より一般的な先読みはトリッキーです

public static void Visit<TItem>(this IEnumerable<TItem> theList, 
         Action<TItem, TItem> visitor) 
{ 
    TItem prev = default(TItem); 
    using (var iterator = theList.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      return; 
     } 
     prev = iterator.Current; 
     while (iterator.MoveNext()) 
     { 
      TItem current = iterator.Current; 
      visitor(prev, current); 
      prev = current; 
     } 
    } 
    visitor(prev, default(TItem)); // Are you sure you want this? 
} 

、正直に言うと...あなたは...私は疑う、おそらくカスタムコレクションを循環バッファのいくつかの並べ替えをしたいと思います。

+0

@Jon Skeet - これのためのthx。最後の行であなたの質問を説明できますか?私の推論は、リストに1つのアイテムしかない場合、私は依然として訪問者にそれを処理させたいということでした。しかしおそらく私は何かを逃している –

+0

@シモン:それはあなたが1つのアイテムがデフォルト値(null、0、何でも)であるペアと1つのアイテムだけが存在する最終的なペアの間でビジターを区別できないことを意味しますは本物"。 –

+0

@ジョン - ああ、はい。いい視点ね。リストを訪れた後、最終要素を処理する必要がありますか?あるいは、特別な旗や価値など何か他のことを考えていましたか? –

1

あなたが間違ったタイプを使用しているようです。シーケンスを索引付けする動作は、指定された索引に1回ずつ到達するまで繰り返します。 IList<T>またはReadOnlyCollection<T>を使用しないでください。

1

テストされていませんが、これはうまくいくと思いますか?訪問が境界を越えると、リストの先頭にループします。

List<string> results = new List<string>(); 
List<string> strings = new List<string>() 
    { "a", "b", "c", "d", "a", "b", "c", "d" }; 
FriendlyEnumerable<string> fe = new FriendlyEnumerable<string>(strings); 
Action<string, string> compareString = 
    new Action<string,string>((s1, s2) => 
     { 
      if (s1 == s2) 
       results.Add(s1 + " == " + s2); 
     }); 
fe.VisitAll(compareString); 
//no results 
fe.VisitAll(compareString, 4); 
//8 results 
1
public static void VisitLookAhead<TItem>(
    this IEnumerable<TItem> source, 
    Action<IEnumerable<TItem>> visitor, 
    int targetSize 
) 
{ 
    if (targetSize <= 1) 
    { 
    throw new Exception("invalid targetSize for VisitLookAhead"); 
    } 

    List<List<TItem>> collections = new List<List<TItem>>(); 

// after 6th iteration with targetSize 6 
//1, 2, 3, 4, 5, 6 <-- foundlist 
//2, 3, 4, 5, 6 
//3, 4, 5, 6 
//4, 5, 6 
//5, 6 
//6 
    foreach(TItem x in source) 
    { 
    collections.Add(new List<TItem>()); 
    collections.ForEach(subList => subList.Add(x)); 
    List<TItem> foundList = collections 
     .FirstOrDefault(subList => subList.Count == targetSize); 
    if (foundList != null) 
    { 
     collections.Remove(foundList); 
     visitor(foundList); 
    } 
    } 

    //generate extra lists at the end - when lookahead will be missing items. 
    foreach(int i in Enumerable.Range(1, targetSize) 
    { 
    collections.ForEach(subList => subList.Add(default(TItem))); 
    List<TItem> foundList = collections 
     .FirstOrDefault(subList => subList.Count == targetSize); 
    if (foundList != null) 
    { 
     collections.Remove(foundList); 
     visitor(foundList); 
    } 
    } 
} 
+0

+1。 THX VM –

4

を我々は拡張メソッドを定義した同様のタスクに実行する場合:

/// <summary> 
/// Projects a window of source elements in a source sequence into target sequence. 
/// Thus 
/// target[i] = 
///  selector(source[i], source[i - 1], ... source[i - window + 1]) 
/// </summary> 
/// <typeparam name="T">A type of elements of source sequence.</typeparam> 
/// <typeparam name="R">A type of elements of target sequence.</typeparam> 
/// <param name="source">A source sequence.</param> 
/// <param name="window">A size of window.</param> 
/// <param name="lookbehind"> 
/// Indicate whether to produce target if the number of source elements 
/// preceeding the current is less than the window size. 
/// </param> 
/// <param name="lookahead"> 
/// Indicate whether to produce target if the number of source elements 
/// following current is less than the window size. 
/// </param> 
/// <param name="selector"> 
/// A selector that derives target element. 
/// On input it receives: 
/// an array of source elements stored in round-robing fashon; 
/// an index of the first element; 
/// a number of elements in the array to count. 
/// </param> 
/// <returns>Returns a sequence of target elements.</returns> 
public static IEnumerable<R> Window<T, R>(
    this IEnumerable<T> source, 
    int window, 
    bool lookbehind, 
    bool lookahead, 
    Func<T[], int, int, R> selector) 
{ 
    var buffer = new T[window]; 
    var index = 0; 
    var count = 0; 

    foreach(var value in source) 
    { 
    if (count < window) 
    { 
     buffer[count++] = value; 

     if (lookbehind || (count == window)) 
     { 
     yield return selector(buffer, 0, count); 
     } 
    } 
    else 
    { 
     buffer[index] = value; 
     index = index + 1 == window ? 0 : index + 1; 

     yield return selector(buffer, index, count); 
    } 
    } 

    if (lookahead) 
    { 
    while(--count > 0) 
    { 
     index = index + 1 == window ? 0 : index + 1; 

     yield return selector(buffer, index, count); 
    } 
    } 
} 

/// <summary> 
/// Projects a window of source elements in a source sequence into a 
/// sequence of window arrays. 
/// </summary> 
/// <typeparam name="T">A type of elements of source sequence.</typeparam> 
/// <typeparam name="R">A type of elements of target sequence.</typeparam> 
/// <param name="source">A source sequence.</param> 
/// <param name="window">A size of window.</param> 
/// <param name="lookbehind"> 
/// Indicate whether to produce target if the number of source elements 
/// preceeding the current is less than the window size. 
/// </param> 
/// <param name="lookahead"> 
/// Indicate whether to produce target if the number of source elements 
/// following current is less than the window size. 
/// </param> 
/// <returns>Returns a sequence of windows.</returns> 
public static IEnumerable<T[]> Window<T>(
    this IEnumerable<T> source, 
    int window, 
    bool lookbehind, 
    bool lookahead) 
{ 
    return source.Window(
    window, 
    lookbehind, 
    lookahead, 
    (buffer, index, count) => 
    { 
     var result = new T[count]; 

     for(var i = 0; i < count; ++i) 
     { 
     result[i] = buffer[index]; 
     index = index + 1 == buffer.Length ? 0 : index + 1; 
     } 

     return result; 
    }); 
} 

これらの関数は、出力を生成するのに役立ちますが、それが好きで使用することができ

public class FriendlyEnumerable<T> : IEnumerable<T> 
{ 
    private IEnumerable<T> _enum; 

    public FriendlyEnumerable(IEnumerable<T> enumerable) 
    {    
     _enum = enumerable; 
    } 

    public void VisitAll(Action<T, T> visitFunc) 
    { 
     VisitAll(visitFunc, 1); 
    } 

    public void VisitAll(Action<T, T> visitFunc, int lookahead) 
    { 
     int index = 0; 
     int length = _enum.Count(); 
     _enum.ToList().ForEach(t => 
     { 
      for (int i = 1; i <= lookahead; i++) 
       visitFunc(t, _enum.ElementAt((index + i) % length)); 
      index++; 
     }); 
    } 

    #region IEnumerable<T> Members 
    public IEnumerator<T> GetEnumerator() 
    { 
     return _enum.GetEnumerator(); 
    } 
    #endregion 
} 

入力要素のウィンドウからの要素

LINQ extensionsも参照してください。