2015-09-04 11 views
28

Ho una semplice implementazione di sequenza di Fibonacci utilizzando BigInteger:IEnumerable <T> Salta sulla sequenza illimitata

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

Si tratta di una sequenza di illimitata come MoveNext() restituisce sempre vero.

Quando viene chiamato utilizzando

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

l'uscita è come previsto (1,1,2,3,5,8, ...)

voglio selezionare 10 articoli, ma a partire da 100 posizione. Ho provato a chiamare tramite

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

ma questo non funziona, come uscite dieci elementi dall'inizio (cioè l'uscita è nuovamente 1,1,2,3,5,8, ...).

I posso saltare chiamando SkipWhile

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

che emette correttamente 10 elementi a partire dall'elemento 100 °.

C'è qualcos'altro che deve/può essere implementato nell'enumeratore per rendere operativo il Skip(...)?

+3

Se siete nella versione corretta di C# (non sono sicuro quale atm) potete usare 'yield' e' yield return' che mi hanno risparmiato un sacco di sforzi, ed elimina la definizione di una nuova classe di enumerazione bc fatta per tu. –

+2

nel tuo caso, l'accesso a 'Current' diverse volte produce risultati diversi. Non dovrebbe. – njzk2

+2

Uhhh, un getter con effetti collaterali! Cattiva. – usr

risposta

49

Skip(n) non accede Current, semplicemente chiama MoveNext()n volte.

Quindi è necessario per eseguire l'incremento in MoveNext(), che è the logical place for that operation anyway:

attuale non si muove la posizione del enumeratore, e le chiamate consecutive al corrente di ritorno lo stesso oggetto fino a quando MoveNext o Reset è chiamato .

+0

Sì, funziona bene, grazie – rbm

5

Spostare la logica nel 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) è semplicemente chiamando MoveNext 10 volte e quindi Current. È inoltre logico che l'operazione venga eseguita in MoveNext anziché corrente.

33

risposta di CodeCaster è a posto - vorrei solo far notare che non si ha realmente bisogno di implementare il proprio enumerabile per qualcosa di simile:

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

    while (true) 
    { 
    yield return current; 

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

il compilatore creare sia l'enumeratore e l'enumerabile per te. Per una semplice enumerazione come questa, la differenza non è poi così grande (basta evitare un sacco di regole), ma se effettivamente hai bisogno di qualcosa di più complicato di una semplice funzione ricorsiva, fa un'enorme differenza.

+4

. Il rendimento è tuo amico. –

Problemi correlati