C# Linq前瞻迭代

C# Linq前瞻迭代,c#,linq,iteration,C#,Linq,Iteration,我正在使用访问者类型模式遍历集合,需要访问列表中的当前项和下一项。目前,我是通过这样一种扩展方法来实现的 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) {

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

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));
        }    
    }    
}
公共无效访问(此IEnumerable列表,操作访问者)
{

对于(i=0;i而言,您似乎使用了错误的类型。为序列编制索引的行为每次都会对序列进行迭代,直到它达到指定的索引。为什么不使用
IList
ReadOnlyCollection

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

var query = original.Zip(original.Skip(1),
                         (current, next) => new { current, next });
这将在序列上迭代两次。一个更好的替代当前扩展方法的方法(顺便说一句,我认为它不起作用,因为IEnumerable没有
Count
属性,并且您试图调用
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?
}
公共静态无效访问(此IEnumerable列表,
行动(访客)
{
TItem prev=默认值(TItem);
使用(var iterator=theList.GetEnumerator())
{
如果(!iterator.MoveNext())
{
返回;
}
prev=迭代器。当前;
while(iterator.MoveNext())
{
TItem current=迭代器.current;
访客(上一位、现任);
prev=当前值;
}
}
visitor(prev,default(TItem));//您确定要这样做吗?
}

老实说,更全面的前瞻更为棘手……我想,您可能需要某种循环缓冲区……可能是一个自定义集合。

未经测试,但我认为这是可行的?当访问超出边界时,它会循环到列表的前面

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
}
公共类友好可枚举:IEnumerable
{
私有IEnumerable _enum;
公共友好可枚举(IEnumerable enumerable)
{            
_枚举=可枚举;
}
公共无效访问权限(操作访问权限)
{
VisitAll(visitFunc,1);
}
public void VisitAll(Action visitFunc,int lookahead)
{
int指数=0;
int length=_enum.Count();
_enum.ToList().ForEach(t=>
{
对于(int i=1;i
{
如果(s1==s2)
结果。添加(s1+“=”+s2);
});
fe.VisitAll(比较);
//没有结果
fe.VisitAll(比较,4);
//8结果
公共静态无效访问lookahead(
这是一个数不清的来源,
行动访客,
int targetSize
)
{
if(targetSize子列表.计数==targetSize);
if(foundList!=null)
{
集合。删除(foundList);
访客(发现名单);
}
}
//在末尾生成额外列表-当前瞻将丢失项目时。
foreach(可枚举范围中的int i(1,targetSize)
{
collections.ForEach(subList=>subList.Add(默认值(TItem));
List foundList=集合
.FirstOrDefault(subList=>subList.Count==targetSize);
if(foundList!=null)
{
集合。删除(foundList);
访客(发现名单);
}
}
}

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

/// <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;
    });
}
//
///将源序列中的源元素窗口投影到目标序列中。
///因此
///目标[i]=
///选择器(源[i]、源[i-1]、…源[i-window+1])
/// 
///源序列的一种元素类型。
///目标序列的一种元素类型。
///源序列。
///窗户大小。
/// 
///如果源元素的数量增加,则指示是否生成目标
///在电流之前小于窗口大小。
/// 
/// 
///如果源元素的数量增加,则指示是否生成目标
///以下电流小于窗口大小。
/// 
/// 
///派生目标元素的选择器。
///在收到输入时:
///一个源元素数组,存储在round robing fashon中;
///第一个元素的索引;
///数组中要计数的元素数。
/// 
///返回目标元素的序列。
公共静态IEnumerable窗口(
这是一个数不清的来源,
int窗口,
布尔回头看,
布尔,向前看,
Func选择器)
{
var buffer=新的T[窗口];
var指数=0;
var计数=0;
foreach(源中的var值)
{
如果(计数<窗口)
{
缓冲区[计数++]=值;
if(lookbehind | |(count==窗口))
{
收益率返回选择器(缓冲区,0,计数);
}
}
其他的
{
缓冲区[索引]=值;
索引=索引+1==窗口?0:索引+1;
收益率返回选择器(缓冲区、索引、计数);
}
}
如果(向前看)
{
而(--计数>0)
{
索引=索引+1==窗口?0:索引+1;
收益率返回选择器(缓冲区、索引、计数);
}
}
}
/// 
///将源序列中的源元素窗口投影到
///窗口数组的序列。
/// 
///源序列的一种元素类型。
///目标序列的一种元素类型。
///源序列。
///窗户大小。
/// 
///如果源元素的数量增加,则指示是否生成目标
///在电流之前小于窗口大小。
/// 
/// 
///如果源元素的数量增加,则指示是否生成目标
///以下电流小于窗口大小。
/// 
///返回一系列窗口。
公共静态IEnumerable窗口(
这是一个数不清的来源,
int窗口,
布尔回头看,
布尔(前瞻)
{
返回源.Window(
窗口,
回头看,
展望未来,
(缓冲区、索引、计数)=>
{
var结果=新的T[计数];
对于(变量i=0;i
这些函数有助于从输入元素窗口生成输出元素


另请参见。

您的代码甚至无法编译。
IEnumerable
上没有名为
Count
的属性(有一个方法组)。@Jason-thx-我编写了
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);
    }
  }
}
/// <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;
    });
}