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()
всегда возвращает истину.
Когда вызывается с помощью
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(...)
Работа?
3 ответа
Skip(n)
не имеет доступа Current
просто звонит MoveNext()
n
раз.
Так что вам нужно выполнить приращение в MoveNext()
, что в любом случае является логическим местом для этой операции:
Current не перемещает позицию перечислителя, и последовательные вызовы Current возвращают один и тот же объект, пока не будет вызван MoveNext или Reset.
Ответ 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;
}
}
Компилятор создаст для вас и перечислитель, и перечислитель. Для простого перечислимого, такого как это, разница не так уж велика (вы просто избегаете тонны шаблонного шаблона), но если вам действительно нужно что-то более сложное, чем простая рекурсивная функция, это имеет огромное значение.
Переместите свою логику в 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
, а не текущий.