IEnumerable Saltar en un número ilimitado de secuencia


Tengo una implementación simple de la secuencia de Fibonacci usando 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();}
    }

Es una secuencia ilimitada ya que el MoveNext() siempre devuelve true.

Cuando se llama usando

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

La salida es la esperada (1,1,2,3,5,8,...)

Quiero seleccionar 10 elementos, pero comenzando en la posición 100. Traté de llamarlo a través de

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

Pero esto no funciona, ya que produce diez elementos desde el principio (es decir, la salida es de nuevo 1,1,2,3,5,8,...).

I puede saltarlo llamando a SkipWhile

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

Que produce correctamente 10 elementos a partir del elemento 100.

¿Hay algo más que necesite/pueda ser implementado en el enumerador para hacer que el Skip(...) funcione?

Author: rbm, 2015-09-04

3 answers

Skip(n) no accede a Current, solo llama MoveNext() n times.

Así que necesitas realizar el incremento en MoveNext(), que es el lugar lógico para esa operación de todos modos:

Current no mueve la posición del enumerador, y las llamadas consecutivas a Current devuelven el mismo objeto hasta que se llame a MoveNext o Reset.

 51
Author: CodeCaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 12:27:08

La respuesta de CodeCaster es acertada-me gustaría señalar que realmente no necesita implementar su propio enumerable para algo como esto:

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

  while (true)
  {
    yield return current;

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

El compilador creará tanto el enumerador como el enumerable para usted. Para un simple enumerable como este la diferencia no es realmente tan grande (solo evita toneladas de repeticiones), pero si realmente necesita algo más complicado que una simple función recursiva, hace una gran diferencia.

 33
Author: Luaan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 12:47:09

Mueve tu lógica a 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) simplemente llama a MoveNext 10 veces, y luego Current. También tiene más sentido lógico hacer la operación en MoveNext, en lugar de actual.

 5
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 12:29:30