Recientemente he estado implementando una implementación de búsqueda de directorio recursiva y estoy usando una Pila para seguir los elementos de ruta. Cuando utilicé string.Join() para unirme a los elementos de la ruta, descubrí que estaban invertidos. Cuando depuré el método, busqué en la pila y encontré que los elementos se invirtieron en la matriz interna de la Pila, es decir, el elemento Push() ed más reciente estaba al principio de la matriz interna, y el menos recientemente Push() El elemento ed estaba al final de la matriz interna. Esto parece ir hacia atrás y muy contrario a la intuición. ¿Alguien puede decirme por qué Microsoft implementaría una pila de esa manera?Implementación de Stack <> en C#
Respuesta
Creo que estás equivocado.
No es que Stack<T>.Push
internamente inserta un elemento al comienzo de su matriz interna (no es así). Más bien, enumera de arriba a abajo, ya que esta es la manera en que uno enumeraría intuitivamente a través de una pila (piense en una pila de panqueques: comienza en la parte superior y avanza hacia abajo).
Si observa los contenidos de una colección desde el depurador de Visual Studio, creo que los mostrará en el orden en que están enumerados, no el orden en que están almacenados internamente *.
Tome una mirada en el método Stack<T>.Push
en Reflector y verá que el código es básicamente exactamente lo que se espera:
// (code to check array size)
this._array[this._size++] = item;
// (code to update internal version number)
Así que la pila se suma internamente nuevos elementos al final de su interior formación. Es la clase Stack<T>.Enumerator
la que te confunde, no la clase Stack<T>
.
* No sé si esto es cierto en general, pero es cierto para Stack<T>
; ver Hans Passant's excellent answer por la razón por la cual.
+1 por ser el primero en responder la pregunta. –
+1, en respuesta al OP, es muy intuitivo para mí ver primero la parte superior de la pila. – jfsk3
Cuando miré la matriz interna de la pila en el depurador, supuse que me mostraría el orden real de los elementos internamente, en lugar de cómo se enumeran a través de la interfaz IEnumerable. Gracias por aclarar eso. –
Lo que describió es la implementación correcta, ya que una pila es una estructura LIFO (último en entrar, primero en salir). Imagínelo como una pila de placas, el último elemento colocado en la pila es el primero eliminado. ¿Has encontrado una pila en otro lugar que sea FIFO?
FIFO sería una cola.
La imagen en la página wiki explica esto muy bien, en realidad http://tinyurl.com/lwtr2 (lo siento por el tinyURL, Stackoverflow no le gusta la URL de la wiki por alguna razón) – Dlongnecker
No sé por qué alguien te votó abajo en esto. Tu respuesta es el libro de texto correcto. – Josh
@Ziplin, enlace roto. – jfsk3
No veo qué importa qué final consideren la parte superior de la pila, siempre y cuando sepa de qué lado se trata. En realidad, tiene más sentido que cuando "empujas" algo hacia la pila, lo estés empujando hacia arriba (comenzando) y moviendo los otros elementos hacia abajo ...
Pero mover las cosas hacia abajo requiere O (n) tiempo para presionar y soltar. Solo ponerlo en la parte superior toma O (1) (amortizado si necesita copiar la matriz en una más grande). Así que hay grandes eficiencias que se obtienen al tener la parte superior de la pila al final de la matriz. –
Respondido por Dan Tao, en realidad no está almacenado de esa manera, solo se enumera de esa manera. – Fosco
Me tenías yendo allí un poco, que de hecho se ve completamente a favor de los bajos. Sin embargo, está sucediendo algo más. La clase Stack <> tiene un visualizador de depuración, llamado System_StackDebugView <>. Es una clase interna, tendrías que mirar con Reflector para verla.
Ese visualizador tiene una propiedad Items, eso es lo que se ve al expandir el nodo en el depurador. Esa propiedad Items usa Stack <> .ToArray(). Que se ve así:
public T[] ToArray()
{
T[] localArray = new T[this._size];
for (int i = 0; i < this._size; i++)
{
localArray[i] = this._array[(this._size - i) - 1];
}
return localArray;
}
Yup, al revés.
Así es como se implementan los métodos push y pops de la pila. Tenga en cuenta que está utilizando el último índice en la matriz, en lugar del primero. Por lo tanto, debe haber algún otro problema para que el suyo retroceda.
public virtual void Push(object obj)
{
if (this._size == this._array.Length)
{
object[] destinationArray = new object[2 * this._array.Length];
Array.Copy(this._array, 0, destinationArray, 0, this._size);
this._array = destinationArray;
}
this._array[this._size++] = obj;
this._version++;
}
public virtual object Pop()
{
if (this._size == 0)
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack"));
}
this._version++;
object obj2 = this._array[--this._size];
this._array[this._size] = null;
return obj2;
}
Y la solución al problema es que Stack <>. ToArray() está devolviendo una copia inversa , en lugar de this._array –
Sí, tienes razón, es el método ToArray, que es extraño y retroactivo de lo que esperaría, pero no veo por qué estarías usando la matriz devuelta como una pila. Pero en cuanto a responder la pregunta, no tengo idea de por qué Microsoft habría hecho eso. – Pieces
Para añadir a las otras respuestas, si en el depurador se desplaza hacia abajo a la parte inferior de la pila <> 's elementos y abrir Raw Ver-> miembros que no son públicas -> _ matriz se puede ver la contenido de la matriz interna real utilizada para contener los elementos y verificar que estén en el orden esperado.
- 1. Cuándo utilizar la colección Stack <T> en C#?
- 2. ¿Por qué Stack <T> y Queue <T> se implementan con una matriz?
- 3. Implementación de IEnumerable <T> en C++/CLI
- 4. incluyendo <xstring>, <cstring>, <string> y <wstring> en C++
- 5. C# Cómo convertir una expresión <Func <SomeType>> en una expresión <Func <OtherType>>
- 6. Ámbito de bibliotecas de C en C++ - <X.h> vs <cX>
- 7. ¿Cuál es la diferencia entre <C-C> y <C-[> en vim?
- 8. C# Acción <> con Func <> parámetro
- 9. C# Ejecución IEquatable <T> .Equal <T>
- 10. C# Expandir lista plana <T> al diccionario <T, ICollection <int>>
- 11. Lazy <T> de implementación y .NET genéricos
- 12. Implementación de Android Parcelable con ArrayList <Object>
- 13. Implementación de la interfaz Java Iterable <E>
- 14. Implementación de Lazy <T> para .NET 3.5
- 15. Java Pair <T,N> implementación de clase
- 16. ¿Cómo combino varias acciones <T> en una sola acción <T> en C#?
- 17. C# - delegar System.Func < >
- 18. <noscript> en <head>
- 19. ¿Por qué <C-PageUp> y <C-PageDown> no funcionan en vim?
- 20. Thread-safe C++ stack
- 21. C++ stack y scope
- 22. C++ Stack Tracing problema
- 23. C# SIP Stack/Library
- 24. Lista <int> en C#
- 25. C# linq en el diccionario <>
- 26. vector <int> :: size_type en C++
- 27. Clasificación Lista <String> en C#
- 28. <%# %> vs <%= %>
- 29. Depuración visual utilizando >>,>,> |, ||, | <, <, <<
- 30. SortedList <>, SortedDictionary <> y Dictionary <>
Parece que quiere una cola y no una pila. –
No, estoy iterando a través del árbol en primer lugar, debe ser una pila, pero eso no es realmente relevante para la pregunta. –
Los hombres reales usan pilas que comienzan en la dirección más alta y crecen. –