Подтвердить что ты не робот

IEnumerable <T> Пропустить неограниченную последовательность

У меня есть простая реализация последовательности Фибоначчи с помощью 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(_));

который правильно выводит 10 элементов, начиная с 100-го элемента.

Есть ли что-то еще, что нужно/может быть реализовано в перечислителе, чтобы сделать работу Skip(...)?

4b9b3361

Ответ 1

Skip(n) не имеет доступа к Current, он просто вызывает MoveNext() n раз.

Итак, вам нужно выполнить приращение в MoveNext(), которое логическое место для этой операции в любом случае:

Ток не перемещает позицию перечислителя, а последовательные вызовы в Current возвращают один и тот же объект до тех пор, пока не вызывается MoveNext или Reset.

Ответ 2

Ответ 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;
  }
}

Компилятор создаст как перечислитель, так и перечислимый для вас. Для простого перечислимого типа разница на самом деле не такая большая (вы просто избегаете тонны шаблона), но если вам действительно нужно что-то более сложное, чем простая рекурсивная функция, это имеет огромное значение.

Ответ 3

Переместите свою логику в 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;
    }
}

Пропуск (10) просто вызывает MoveNext 10 раз, а затем Current. Кроме того, логичнее было бы выполнить операцию в MoveNext вместо текущей.