首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linq - lookahead迭代

Linq - lookahead迭代
EN

Stack Overflow用户
提问于 2011-06-28 22:26:51
回答 5查看 2.5K关注 0票数 9

我正在使用访问者类型的模式遍历一个集合,并且需要访问列表中的当前项和下一项。目前,我是通过如下的扩展方法来实现的

代码语言:javascript
复制
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”项。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-06-28 22:35:12

假设您使用的是.NET 4,那么您可以使用Zip来完成相同的任务:

代码语言:javascript
复制
var query = original.Zip(original.Skip(1),
                         (current, next) => new { current, next });

这将对序列进行两次迭代。一个比你当前的扩展方法更好的替代方法(顺便说一句,我不相信它会起作用,因为IEnumerable没有Count属性,而且你也试图把theList作为一个方法来调用……)应该是这样的:

代码语言:javascript
复制
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?
}

老实说,更通用的先行预测更棘手……你可能需要某种循环缓冲区,我猜...可能是一个自定义集合。

票数 10
EN

Stack Overflow用户

发布于 2014-03-07 23:10:10

当我们遇到类似的任务时,我们定义了一个扩展方法:

代码语言:javascript
复制
/// <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;
    });
}

这些函数有助于从输入元素的窗口中生成输出元素。

另请参见LINQ extensions

票数 4
EN

Stack Overflow用户

发布于 2011-06-28 22:32:37

您似乎使用了错误的类型。对序列进行索引的操作将对其进行迭代,直到它每次都达到指定的索引。为什么不使用IList<T>ReadOnlyCollection<T>

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6508060

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档