IEnumerable <t>跳过无限序列

时间:2015-09-04 12:24:27

标签: c# linq fibonacci skip

我使用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()始终返回true。

使用

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

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

我想选择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个元素。

是否还需要/可以在枚举器中实现其他内容以使Skip(...)有效?

3 个答案:

答案 0 :(得分:51)

Skip(n)无法访问Current,只需拨打MoveNext() n次。

因此,您需要在MoveNext()中执行增量,即the logical place for that operation anyway

  

当前不会移动枚举器的位置,并且对Current的连续调用将返回相同的对象,直到调用MoveNext或Reset。

答案 1 :(得分:33)

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;
  }
}

编译器将为您创建枚举器和枚举。对于这样一个简单的可枚举,差异并不是那么大(你只需要避免大量的样板),但如果你真的需要比简单的递归函数更复杂的东西,它会产生巨大的差异。

答案 2 :(得分:5)

将您的逻辑移至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中完成操作更有逻辑意义,而不是当前。