Come si progetta un enumeratore che restituisce (teoricamente) una quantità infinita di articoli?

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

  •  10-07-2019
  •  | 
  •  

Domanda

Sto scrivendo un codice simile a questo:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}

Ovviamente, questo metodo non tornerà mai più. (Il compilatore C # lo consente silenziosamente, mentre R # mi dà l'avvertimento & Quot; La funzione non restituisce mai & Quot; .)

In generale, è cattivo fornire un enumeratore che restituisce un numero infinito di elementi, senza fornire un modo per smettere di enumerare?

Ci sono considerazioni speciali per questo scenario? Mem? Perf? Altri gotchas?

Se forniamo sempre una condizione di uscita, quali sono le opzioni? Per esempio:

  • un oggetto di tipo T che rappresenta il confine inclusivo o esclusivo
  • a Predicate<T> continue (come TakeWhile)
  • un conteggio (come Take)
  • ...

Dovremmo fare affidamento sugli utenti che chiamano Take(...) / TakeWhile(...) dopo Unfold(...)? (Forse l'opzione preferita, poiché sfrutta le conoscenze Linq esistenti.)

Risponderesti diversamente a questa domanda se il codice fosse pubblicato in un'API pubblica, così com'è (generico) o come implementazione specifica di questo modello?

È stato utile?

Soluzione

Finché documentate molto chiaramente che il metodo non finirà mai di iterare (il metodo stesso ritorna molto rapidamente, ovviamente) allora penso che vada bene. In effetti, può rendere alcuni algoritmi molto più ordinati. Non credo che ci siano implicazioni significative di memoria / perf - anche se si fa riferimento a un & Quot; costoso & Quot; oggetto all'interno del tuo iteratore, tale riferimento verrà acquisito.

Esistono sempre modi per abusare delle API: finché i tuoi documenti sono chiari, penso che vada bene.

Altri suggerimenti

  

" In generale, è un cattivo desing   per fornire un enumeratore che ritorna   una quantità infinita di oggetti, senza   fornendo un modo per smettere di enumerare? "

Il consumatore del codice, può sempre smettere di enumerare (usando l'interruzione per esempio o altri mezzi). Se l'enumeratore restituisce una sequenza infinita, ciò non significa che il client dell'enumeratore sia in qualche modo costretto a non interrompere mai l'enumerazione, in realtà non è possibile creare un enumeratore che è garantito per essere completamente elencato da un client.

  

Dovremmo fare affidamento sulla chiamata degli utenti   Take (...) / TakeWhile (...) dopo   Aprire (...)? (Forse il preferito   opzione, poiché sfrutta esistente   Conoscenza Linq.)

Sì, fintanto che nella documentazione si specifica chiaramente che l'enumeratore ritorna e la sequenza infinita e l'interruzione dell'enumerazione sono responsabilità del chiamante, tutto dovrebbe andare bene.

Restituire sequenze infinite non è una cattiva idea, i linguaggi di programmazione funzionale lo fanno da molto tempo ormai.

Sono d'accordo con Jon. Il compilatore trasforma il tuo metodo in classe implementando una macchina a stati semplici che mantiene il riferimento al valore corrente (ovvero il valore che verrà restituito tramite la proprietà corrente). Ho usato questo approccio più volte per semplificare il codice. Se documenti chiaramente il comportamento del metodo, dovrebbe funzionare bene.

Non vorrei usare un enumeratore infinito in un'API pubblica. I programmatori C #, me compreso, sono troppo abituati al ciclo foreach. Ciò sarebbe anche coerente con .NET Framework; notare come i metodi Enumerable.Range e Enumerable.Repeat accettano un argomento per limitare il numero di elementi nell'enumerabile. Microsoft ha scelto di utilizzare Enumerable.Repeat (& Quot; & Quot ;, 10) invece di Enumerable.Repeat (& Quot; & Quot;). Prendi (10) per evitare l'enumerazione infinita e I aderirebbe alle loro scelte progettuali.

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