2012-01-06 11 views
7

Capisco come funziona Stack() e Stack<T>, ma in realtà non riesco a vedere nessuno scenario in cui un array, List<T> o IEnumerable<T> non è una scelta migliore e più semplice.Quando utilizzare la raccolta Stack <T> in C#?

Qualcuno può fornirmi un esempio reale di utilizzo di Stack<T>?

+3

Precisamente quando si vuole far rispettare il fatto che la vostra collezione viene manipolata come una pila, che Lista non può garantire. IEnumerable è una cosa completamente diversa. –

+0

OK. Potete fornirmi un esempio di uno scenario in cui desidero il comportamento dello stack? – jhovgaard

+0

hmm 'Stack La funzionalità di' 'sembra davvero un sottoinsieme di' Lista ', quindi sono d'accordo che non sembra la raccolta più utile. L'applicazione del comportamento dello stack è necessaria solo se esposta pubblicamente. Ma in quel caso di solito è una buona idea esporre una specie di wrapper sulla collezione interna, quindi non sembra davvero molto utile neanche per quello. Quindi vedrei sicuramente l'uso per un'interfaccia '' IStack ', ma non tanto per una semplice classe di raccolta' Stack '. – CodesInChaos

risposta

9

Idealmente si utilizzano, o si creano secondo necessità, classi che riflettono il modo in cui le cose funzionano nel mondo reale, le cose che si stanno modellando nel codice. Tali classi ci danno un livello di astrazione in modo che possiamo codificare in termini di ciò che stiamo modellando/simulando. Inoltre, quando si codifica una cosa complessa, l'utilizzo di un paradigma familiare aiuta. Per arguire: Oh, questa classe Fuzzinator usa uno Stack. So cosa è una pila e come funziona.

In secondo luogo, questa classe di livello superiore di astrazione ci fornisce il codice che funziona (presupponiamo che il framework .NET sia stato testato) e ci risparmia il tempo e il dolore di reinventare la ruota.

In terzo luogo, il codice è più facile da leggere, più facile da capire, più facile da modificare e così via. È più manutenibile.

L'utilizzo di classi con funzionalità più raffinate consente di limitare il modo in cui si potrebbe rovinare l'utilizzo.

Nel complesso l'applicazione è semplicemente meglio quando è codificata a livelli appropriati di astrazione.

Lo stack è una di queste classi.

La calcolatrice HP-41X esegue la sua aritmetica utilizzando una pila. Questo metodo di calcolo è chiamato RPN - Notazione polacca inversa.

Se stavo simulando una caffetteria, lo Stack sarebbe perfetto per quella pila di piatti. I piatti si accendono e si staccano dall'alto. Non al centro, non alla fine; solo il top. Una pila. Posso solo le lastre Push() e Pop() che rendono il codice più semplice e chiaro.

In alternativa, immaginare la codifica con l'equivalente C# di particelle subatomiche - raccolta generica o IEnumerable generico, ecc. Finisco per usare metodi e proprietà di utilità generale con nomi generali con numeri a variabili multiple di parametri che nell'aggregato il fatto che sto impilando piatti.

+0

Mi hai appena dato un "Oh, ora ho capito!" - esperienza. Grazie! :-) – jhovgaard

2

Se si esegue expression evaluation e l'analisi della sintassi, lo stack potrebbe essere una struttura dati utile.

1

Heres un modo che ho usato Stack:

ho una procedura guidata come pagina in cui ci sono 5 controlli utente che devono essere dispalyed ed elaborati in una sequenza specifica, e l'utente dovrebbe essere in grado in qualsiasi momento per tornare all'ultima pagina in ordine.

Uso una macchina di stato basata sullo stack per tracciare l'avanzamento degli utenti attraverso la procedura guidata.

Ogni volta che si spostano in avanti, spingo lo stato della macchina a stati in uno stack di sessione memorizzato e, se richiedono di tornare indietro, sposto il valore superiore e imposto la macchina sul nuovo valore di stack superiore. (Che a sua volta, display e carichi il corretto controllo)

un altro:

ho dovuto costruire un'installazione domanda di alcune applicazioni server molto personalizzate. Quello che ho finito è stato interrompere ogni passaggio dell'installazione nel proprio componente generico, cioè un componente per copiare un file, un componente per scrivere un valore di registro, ecc. Ogni compontent aveva dei metodi: fai l'azione di installazione, annulla l'azione di installazione.

Ogni fase dell'installazione, il componente viene inserito in una pila.

Ora, in qualsiasi momento ho un errore, ci limitiamo a far scomparire ogni componente dallo stack ed eseguire l'azione di annullamento fornendoci transazioni di installazione a grana fine.

Lo stack è tuo amico!

0

Direi se stai modellando qualcosa che è concettualmente una pila. Dì che stai modellando un piccolo ma profondo cassetto e stai mettendo i libri nel cassetto. Questo seguirà un paradigma "first in, last out", che è il punto di uno stack in generale.

Non si dispone di un elenco di libri o di un qualche tipo di enumerazione - si dispone di un ordinamento specifico di essi ... vale a dire, l'opposto dell'ordine in cui sono stati aggiunti.

3

Depth first tree traversal. Al contrario di una coda, per l'ampiezza del primo attraversamento dell'albero.


Gestione della presentazione di schermate diverse a un utente mentre le naviga intorno. Mostrare uno schermo lo spinge in pila, facendolo tornare indietro. Disegna lo schermo superiore.


Quando si desidera una raccolta in cui è possibile aggiungere le cose e sapere sempre quando si ottiene qualcosa fuori è la più recente aggiunta.


Implementazione della funzionalità Annulla/Ripristina.

+0

Inoltre, credo che sia ciò che * inserisce il demografico pushy qui * quando si unisce a una linea. –

2

È possibile riscrivere i metodi ricorsivi in ​​quanto le controparti iterative sono molto più semplici controllando le persone locali dentro e fuori dallo stack. Controlla il numero Jon Skeet usando lo stack here.

+1

+1 per discutere del caso generale. Vorrei aggiungere che questo approccio è utile se si dispone di un algoritmo profondamente ricorsivo, poiché consente di recurse in profondità senza sovraccaricare lo stack di chiamate: "Le istanze Stack " sono, ovviamente, archiviate nell'heap. – phoog

1

Stack e coda utilizzano internamente un array. Se si utilizzavano gli array in modi intelligenti, è molto probabile che li si sia già utilizzati in uno stack o in coda come se fossero di moda. Di solito è necessario decidere tra Stack e Queue. Un esempio tipico in cui è necessaria una pila è depth first search. Se si modifica la raccolta in una coda, è stata implementata una ricerca per ampiezza.

Un altro esempio è il multithreading pesante in cui si passano dati tra un produttore e un consumatore tramite uno stack se l'ordine di elaborazione non è rilevante. La logica alla base di questo è che se molti dati verranno elaborati, è meglio per la CPU utilizzare l'ultimo chunk di dati aggiunti per un'ulteriore elaborazione sull'altro thread per ottenere una migliore localizzazione della cache.

E l'elenco potrebbe continuare ....

+0

Ho l'impressione che gli stack e le code siano di solito implementati con liste collegate. – SundayMonday

+1

Almeno il BCL .NET esegue l'accodamento di code e stack internamente con gli array. Gli elenchi collegati non funzionano bene in .NET a causa del maggiore consumo di memoria e di un grafico a oggetti più complesso che renderà i GC più costosi. Con solitamente intendi C++ o ...? Dal momento che lo Stack e la coda di solito non sono usati in un modo per aggiungere o rimuovere elementi nelle liste intermedie non hanno molto senso. –

1

pile sono frequentemente utilizzate per gli algoritmi di analisi del testo, come ad esempio la valutazione di "4 + 5 + 6". Per un esempio reale di un'applicazione che utilizza gli stack per analizzare il testo, vedere HTMLAgilityPack. Questo componente viene utilizzato per analizzare HTML e include il codice sorgente, in modo da poter vedere come e dove vengono utilizzati gli stack ...

0

Stack<T> La funzionalità sembra davvero un sottoinsieme di List<T> (con alcuni metodi rinominati) , quindi sono d'accordo che non sembra la raccolta più utile di per sé. Se utilizzato internamente in un algoritmo, List<T> può facilmente sostituirlo, anche se potrebbe essere leggermente meno idiomatico.

L'applicazione del comportamento dello stack è necessaria solo se esposta pubblicamente. Ma in quel caso è di solito un'idea migliore per esporre un qualche tipo di wrapper sulla raccolta interna, quindi non sembra molto utile neanche per quello. Certamente userò un'interfaccia IStack<T>, ma non così tanto per un classe di raccolta semplice Stack<T>.

La mia conclusione è che non avrei incluso una classe Stack<T> nel framework, solo un'interfaccia IStack<T>. Generalmente le collezioni BCL non mi sembrano molto ben pensate.

ConcurrentStack<T> d'altra parte sembra molto più utile.

+2

Se dovessi definire un'interfaccia per 'IStack ' non includeresti almeno una classe che l'ha implementata? (cioè la classe 'Stack '). –

+0

@George Penso che la classe sarebbe piuttosto un wrapper su un'altra raccolta simile a 'ReadOnlyCollection '.O sarebbe anche un'implementazione esplicita dell'interfaccia su 'Lista ', o entrambi. – CodesInChaos

+1

Se "Stack " è un wrapper o meno è solo un dettaglio di implementazione. Direi sicuramente che ottenere l'Elenco per implementarlo porterebbe a una classe orribile, non vorrei davvero i metodi Push e Pop su un elenco. Preferirei avere un 'List ' per gli elenchi e uno 'Stack ' per gli stack. –

1

stack sono utili quando si converte un'espressione da notazione infisso a notazione prefisso. Per esempio:

a + b a (+ a b)

-1

Tenere traccia della Storia Browser è un uso per lo stack. Inoltre, "annulla" per le modifiche, ecc. Analogamente, alcuni sistemi di controllo del magazzino utilizzano stack e code per la gestione dell'ordine in cui gli articoli devono essere selezionati per le spedizioni.

Problemi correlati