Search code examples
c#linqfibonacciskip

IEnumerable<T> Skip on unlimited sequence


I have a simple implementation of Fibonacci sequence using 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();}
    }

It is an unlimited sequence as the MoveNext() always returns true.

When called using

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

the output is as expected (1,1,2,3,5,8,...)

I want to select 10 items but starting at 100th position. I tried calling it via

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

but this does not work, as it outputs ten elements from the beginning (i.e. the output is again 1,1,2,3,5,8,...).

I can skip it by calling SkipWhile

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

which correctly outputs 10 elements starting from the 100th element.

Is there something else that needs/can be implemented in the enumerator to make the Skip(...) work?


Solution

  • Skip(n) doesn't access Current, it just calls MoveNext() n times.

    So you need to perform the increment in MoveNext(), which is 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.