C # trarrebbe vantaggio dalle distinzioni tra tipi di enumeratori, come gli iteratori C ++?

StackOverflow https://stackoverflow.com/questions/1634934

  •  06-07-2019
  •  | 
  •  

Domanda

Ho pensato al metodo IEnumerator.Reset(). Ho letto nella documentazione MSDN che è lì solo per l'interoperabilità COM. Come programmatore C ++ mi sembra un IEnumerator che supporta Reset è ciò che definirei forward iterator , mentre un PrintContents che non supporta <=> è in realtà un input iterator .

Quindi la prima parte della mia domanda è: questa comprensione è corretta?

La seconda parte della mia domanda è: sarebbe di qualche beneficio in C # se si distinguesse tra iteratori di input e iteratori forward (o " enumeratori " se preferisci)? Non aiuterebbe ad eliminare un po 'di confusione tra i programmatori, come quello che si trova in questa Quindi domanda sulla clonazione di iteratori ?

EDIT: chiarimenti sugli iteratori forward e input. Un iteratore di input garantisce che puoi enumerare i membri di una raccolta (o da una funzione del generatore o un flusso di input) solo una volta. Questo è esattamente il modo in cui IEnumerator funziona in C #. La possibilità di enumerare o meno una seconda volta è determinata dal fatto che sia supportato o meno <=>. Un iteratore diretto, non ha questa limitazione. Puoi enumerare i membri tutte le volte che vuoi.

Alcuni programmatori C # non comprendono perché un <=> non possa essere utilizzato in modo affidabile in un algoritmo multipass. Considera il caso seguente:

void PrintContents(IEnumerator<int> xs)
{
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
  iter.Reset();
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
}

Se chiamiamo <=> in questo contesto, nessun problema:

List<int> ys = new List<int>() { 1, 2, 3 }
PrintContents(ys.GetEnumerator()); 

Comunque guarda quanto segue:

IEnumerable<int> GenerateInts() {   
  System.Random rnd = new System.Random();
  for (int i=0; i < 10; ++i)
    yield return Rnd.Next();
}

PrintContents(GenerateInts());

Se il <=> supportato <=>, in altre parole supportasse algoritmi multi-pass, ogni volta che si scorreva sulla raccolta sarebbe diverso. Ciò sarebbe indesiderabile, perché sarebbe un comportamento sorprendente. Questo esempio è un po 'falso, ma si verifica nel mondo reale (ad es. Lettura da flussi di file).

È stato utile?

Soluzione

Domanda interessante. La mia opinione è che ovviamente C # beneficerebbe . Tuttavia, non sarebbe facile aggiungere.

La distinzione esiste in C ++ per via del suo sistema di tipi molto più flessibile. In C #, non hai un modo generico solido per clonare oggetti, che è necessario per rappresentare gli iteratori in avanti (per supportare l'iterazione multi-pass). E, naturalmente, perché ciò sia veramente utile, dovresti anche supportare iteratori / enumeratori bidirezionali e ad accesso casuale. E per farli funzionare tutti senza problemi, hai davvero bisogno di una qualche forma di digitazione anatra, come hanno i modelli C ++.

Alla fine, gli ambiti dei due concetti sono diversi.

In C ++, gli iteratori dovrebbero rappresentare tutto ciò che è necessario sapere su un intervallo di valori. Dato un paio di iteratori, non ho bisogno del contenitore originale. Posso ordinare, posso cercare, posso manipolare e copiare elementi quanto mi pare. Il contenitore originale è fuori dall'immagine.

In C #, gli enumeratori non sono fatti per fare altrettanto. In definitiva, sono progettati per farti scorrere la sequenza in modo lineare.

Per quanto riguarda Reset(), è ampiamente riconosciuto che è stato un errore aggiungerlo in primo luogo. Se ha funzionato ed è stato implementato correttamente, quindi sì, potresti dire che il tuo enumeratore era analogo agli iteratori di inoltro, ma in generale, è meglio ignorarlo come errore. E quindi tutti gli enumeratori sono simili solo agli iteratori di input.

Purtroppo.

Altri suggerimenti

Reset è stato un grosso errore. Chiamo shenanigans su IEnumerable<T>. Secondo me, il modo corretto di riflettere la distinzione che stai facendo tra & Quot; forward iterators & Quot; e " input iteratori " nel sistema di tipi .NET si distingue tra IEnumerator<T> e <=>.

Vedi anche questo risposta , in cui Eric Lippert di Microsoft (in modo non ufficiale, senza dubbio, il mio punto è solo che è qualcuno con più credenziali di quanto devo dichiarare che si trattava di un errore di progettazione) fa un punto simile nei commenti. Vedi anche il suo fantastico blog .

Proveniente dalla prospettiva C #:

Non usi quasi mai direttamente IEnumerator. Di solito fai un'istruzione foreach, che prevede un IEnumerable.

IEnumerable _myCollection;
...
foreach (var item in _myCollection) { /* Do something */ }

Non passi neanche Reset(). Se si desidera passare una raccolta che richiede iterazione, si passa <=>. Poiché <=> ha una singola funzione, che restituisce un <=>, può essere utilizzata per iterare la raccolta più volte (più passaggi).

Non è necessaria una funzione <=> su <=> perché se vuoi ricominciare da capo, butti via quello vecchio (immondizia raccolta) e ne ottieni uno nuovo.

Il framework .NET trarrebbe enormi benefici se ci fosse un modo per chiedere a IEnumerator<T> quali abilità potrebbe supportare e quali promesse potrebbe fare. Tali funzionalità sarebbero utili anche in IEnumerable<T>, ma essere in grado di porre le domande a un enumeratore consentirebbe al codice che può ricevere un enumeratore da wrapper come ReadOnlyCollection di utilizzare la raccolta sottostante in modi migliori senza dover coinvolgere il wrapper.

Dato qualsiasi enumeratore per una collezione che può essere enumerata nella sua interezza e non è troppo grande, si potrebbe produrre da essa un ReadOnlyCollection<T> che produrrebbe sempre la stessa sequenza di oggetti (in particolare l'insieme di oggetti rimanenti nell'enumeratore) leggendo l'intero contenuto in un array, disponendo e scartando l'enumeratore e ottenendo un enumeratore dall'array (usando quello al posto dell'enumeratore abbandonato originale), avvolgendo l'array in <=> e restituendolo . Sebbene un tale approccio funzionerebbe con qualsiasi tipo di raccolta numerosa che soddisfi i criteri di cui sopra, sarebbe terribilmente inefficiente con la maggior parte di essi. Avere un modo per chiedere a un enumeratore di produrre i suoi rimanenti contenuti in un <=> immutabile consentirebbe a molti tipi di enumeratori di eseguire l'azione indicata in modo molto più efficiente.

Non credo. Chiamerei IEnumerable un iteratore di inoltro e un iteratore di input. Non ti consente di tornare indietro o modificare la raccolta sottostante. Con l'aggiunta della parola chiave foreach, gli iteratori sono quasi un non pensato il più delle volte.

Opinione: La differenza tra gli iteratori di input ( ottieni ciascuno ) rispetto agli iteratori di output ( fai qualcosa per ciascuno ) è troppo banale per giustificare un'aggiunta al framework. Inoltre, per eseguire un iteratore di output, è necessario passare un delegato all'iteratore. L'iteratore di input sembra più naturale per i programmatori C #.

C'è anche IList<T> se il programmatore desidera un accesso casuale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top