IEnumerable<T> 跳过无限序列

IEnumerable<T> Skip on unlimited sequence

我有一个使用 BigInteger 的斐波那契数列的简单实现:

internal class FibonacciEnumerator : IEnumerator<BigInteger>
    {
        private BigInteger _previous = 1;
        private BigInteger _current = 0;

        public void Dispose(){}

        public bool MoveNext() {return true;}

        public void Reset()
        {
            _previous = 1;
            _current = 0;
        }

        public BigInteger Current
        {
            get
            {
                var temp = _current;
                _current += _previous;
                _previous = temp;
                return _current;
            }
        }

        object IEnumerator.Current { get { return Current; }
        }
    }

    internal class FibonacciSequence : IEnumerable<BigInteger>
    {
        private readonly FibonacciEnumerator _f = new FibonacciEnumerator();

        public IEnumerator<BigInteger> GetEnumerator(){return _f;}

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

这是一个无限序列,因为MoveNext()总是returns为真。

调用时使用

var fs = new FibonacciSequence();
fs.Take(10).ToList().ForEach(_ => Console.WriteLine(_));

输出符合预期 (1,1,2,3,5,8,...)

我想要 select 10 个项目,但从第 100 个位置开始。我尝试通过

调用它
fs.Skip(100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));

但这不起作用,因为它从头开始输出十个元素(即输出又是 1,1,2,3,5,8,...)。

可以通过调用SkipWhile

跳过它
fs.SkipWhile((b,index) => index < 100).Take(10).ToList().ForEach(_ => Console.WriteLine(_));

从第 100 个元素开始正确输出 10 个元素。

在枚举器中是否有其他 needs/can 可以实现 Skip(...) 的东西?

Skip(n) 不访问 Current,它只是调用 MoveNext() n 次。

所以需要在MoveNext()进行自增,即the logical place for that operation anyway:

Current does not move the position of the enumerator, and consecutive calls to Current return the same object until either MoveNext or Reset is called.

将您的逻辑移至 MoveNext:

public bool MoveNext() 
{
    var temp = _current;
     _current += _previous;
     _previous = temp;
    return true;
}

public void Reset()
{
    _previous = 1;
    _current = 0;
}

public BigInteger Current
{
    get
    {
        return _current;
    }
}

Skip(10) 只是调用 MoveNext 10 次,然后调用 Current。在 MoveNext 中完成操作也比在当前

中完成操作更合乎逻辑。

CodeCaster 的回答很准确——我只是想指出,您并不真的需要为这样的事情实现自己的可枚举:

public IEnumerable<BigInteger> FibonacciSequence()
{
  var previous = BigInteger.One;
  var current = BigInteger.Zero;

  while (true)
  {
    yield return current;

    var temp = current;
    current += previous;
    previous = temp;
  }
}

编译器将为您创建枚举数和可枚举数。对于像这样的简单枚举,区别并不是那么大(你只是避免了大量的样板),但如果你真的需要比简单的递归函数更复杂的东西,那就有很大的不同了。