Necesito moverme hacia atrás a través de una matriz, así que tengo un código como este:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
¿Existe una forma mejor de hacer esto?
Actualización: esperaba que tal vez C # tuviera algún mecanismo incorporado para esto como:
foreachbackwards (int i in myArray)
{
// so easy
}
Actualización 2: hay mejores formas. Rune se lleva el premio con:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
que se ve aún mejor en C normal (gracias a Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
Respuestas:
Si bien es cierto que es un poco oscuro, diría que la forma más agradable tipográficamente de hacer esto es
fuente
En C ++, básicamente tiene la opción entre iterar usando iteradores o índices. Dependiendo de si tiene una matriz simple o una
std::vector
, utiliza diferentes técnicas.Usando std :: vector
Usando iteradores
C ++ te permite hacer esto usando
std::reverse_iterator:
Usando índices
El tipo entero sin signo que devuelve
std::vector<T>::size
es no siemprestd::size_t
. Puede ser mayor o menor. Esto es crucial para que funcione el bucle.Funciona, ya que los valores de los tipos integrales sin signo se definen mediante módulo su recuento de bits. Por lo tanto, si está estableciendo
-N
, terminará en(2 ^ BIT_SIZE) -N
Usando matrices
Usando iteradores
Estamos usando
std::reverse_iterator
para hacer la iteración.Usando índices
Podemos usar con seguridad
std::size_t
aquí, a diferencia de lo anterior, ya quesizeof
siempre devuelvestd::size_t
por definición.Evitar trampas con sizeof aplicado a punteros
En realidad, la forma anterior de determinar el tamaño de una matriz apesta. Si a es en realidad un puntero en lugar de una matriz (lo que sucede con bastante frecuencia y los principiantes lo confundirán), fallará silenciosamente. Una mejor manera es usar lo siguiente, que fallará en tiempo de compilación, si se le da un puntero:
Funciona obteniendo primero el tamaño de la matriz pasada y luego declarando devolver una referencia a una matriz de tipo char del mismo tamaño.
char
se define para tenersizeof
de: 1. Entonces la matriz devuelta tendrá unsizeof
de: N * 1, que es lo que estamos buscando, con solo evaluación del tiempo de compilación y cero sobrecarga de tiempo de ejecución.En lugar de hacer
Cambie su código para que ahora lo haga
fuente
end
ybegin
en orden inverso?En C # , usando Visual Studio 2005 o posterior, escriba 'forr' y presione [TAB] [TAB] . Esto se expandirá a un
for
bucle que retrocede a través de una colección.Es tan fácil equivocarse (al menos para mí), que pensé que sería una buena idea incluir este fragmento.
Dicho esto, me gusta
Array.Reverse()
/Enumerable.Reverse()
y luego iterar hacia adelante mejor: indican más claramente la intención.fuente
Yo siempre prefiero código claro contra el ' tipográficamente complacer código'. Por lo tanto, siempre usaría:
Puede considerarlo como la forma estándar de realizar un bucle hacia atrás.
Solo mis dos centavos ...
fuente
En C # usando Linq :
fuente
Definitivamente, esa es la mejor manera para cualquier arreglo cuya longitud sea un tipo integral con signo. Para matrices cuyas longitudes son un tipo integral sin signo (por ejemplo, una
std::vector
en C ++), entonces necesita modificar ligeramente la condición final:Si acaba de decir
i >= 0
, esto siempre es cierto para un entero sin signo, por lo que el ciclo será un ciclo infinito.fuente
Me parece bien. Si el indexador no estaba firmado (uint, etc.), es posible que deba tenerlo en cuenta. Llámame perezoso, pero en ese caso (sin firmar), podría usar una contravariable:
(de hecho, incluso aquí deberías tener cuidado con casos como arr.Length = uint.MaxValue ... tal vez a! = en algún lugar ... por supuesto, ¡ese es un caso muy poco probable!)
fuente
En CI me gusta hacer esto:
Ejemplo de C # agregado por MusiGenesis:
fuente
La mejor manera de hacer eso en C ++ es probablemente usar adaptadores de iterador (o mejor, rango), que transformarán la secuencia de manera perezosa a medida que se atraviesa.
Básicamente,
Muestra el rango "rango" (aquí, está vacío, pero estoy bastante seguro de que puede agregar elementos usted mismo) en orden inverso. Por supuesto, simplemente iterar el rango no es de mucha utilidad, pero pasar ese nuevo rango a algoritmos y demás es bastante bueno.
Este mecanismo también se puede utilizar para usos mucho más potentes:
Calculará perezosamente el rango "rango", donde la función "f" se aplica a todos los elementos, los elementos para los que "p" no es verdadera se eliminan y finalmente se invierte el rango resultante.
La sintaxis de tubería es la OMI más legible, dado su infijo. La actualización pendiente de revisión de la biblioteca Boost.Range implementa esto, pero también es bastante simple hacerlo usted mismo. Es aún más interesante con un lambda DSEL para generar la función f y el predicado p en línea.
fuente
fuente
Prefiero un bucle while. Es más claro para mí que disminuir
i
en la condición de un bucle forfuente
Usaría el código en la pregunta original, pero si realmente quisieras usar foreach y tener un índice entero en C #:
fuente
Voy a intentar responder mi propia pregunta aquí, pero tampoco me gusta esto:
fuente
NOTA: Esta publicación terminó siendo mucho más detallada y, por lo tanto, fuera de tema, me disculpo.
Dicho esto, mis compañeros lo leen y creen que es valioso "en algún lugar". Este hilo no es el lugar. Agradecería sus comentarios sobre dónde debería ir esto (soy nuevo en el sitio).
De todos modos, esta es la versión C # en .NET 3.5, lo cual es sorprendente porque funciona en cualquier tipo de colección utilizando la semántica definida. Esta es una medida predeterminada (¡reutilización!), No el rendimiento o la minimización del ciclo de la CPU en el escenario de desarrollo más común, aunque eso nunca parece ser lo que sucede en el mundo real (optimización prematura).
*** Método de extensión que trabaja sobre cualquier tipo de colección y realiza una acción que el delegado espera un valor único del tipo, todo ejecutado sobre cada elemento a la inversa **
Requiere 3.5:
¿Versiones anteriores de .NET o desea comprender mejor los componentes internos de Linq? Sigue leyendo .. O no ..
SUPUESTO: En el sistema de tipos .NET, el tipo Array hereda de la interfaz IEnumerable (no el IEnumerable genérico solo IEnumerable).
Esto es todo lo que necesita para iterar de principio a fin, sin embargo, desea moverse en la dirección opuesta. Como IEnumerable funciona en Array de tipo 'objeto', cualquier tipo es válido,
MEDIDA CRÍTICA: Asumimos que si puede procesar cualquier secuencia en orden inverso, es 'mejor' que solo podrá hacerlo en números enteros.
Solución a para .NET CLR 2.0-3.0:
Descripción: Aceptaremos cualquier instancia de implementación de IEnumerable con el mandato de que cada instancia que contiene sea del mismo tipo. Entonces, si recibimos una matriz, la matriz completa contiene instancias de tipo X. Si cualquier otra instancia es de un tipo! = X, se lanza una excepción:
Un servicio singleton:
clase pública ReverserService {private ReverserService () {}
[TestFixture] clase pública Testing123 {
fuente