c# - resta - suma de n numeros en c++ con while
¿Cuál es la mejor manera de hacer un ciclo hacia atrás en C/C#/ C++? (15)
En C # usando Linq :
foreach(var item in myArray.Reverse())
{
// do something
}
Necesito retroceder en una matriz, así que tengo un código como este:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
¿Hay una mejor manera de hacer esto?
Actualización: Tenía la esperanza de 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 toma 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 regular (gracias a Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
En C ++ usted básicamente tiene la opción de iterar usando iteradores o índices. Dependiendo de si tiene una matriz simple o una std::vector
, usa diferentes técnicas.
Usando std :: vector
Usando iteradores
C ++ le permite hacer esto usando std::reverse_iterator:
for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
/* std::cout << *it; ... */
}
Usando índices
El tipo integral sin signo devuelto por std::vector<T>::size
no siempre es std::size_t
. Puede ser mayor o menos. Esto es crucial para que el bucle funcione.
for(std::vector<int>::size_type i = someVector.size() - 1;
i != (std::vector<int>::size_type) -1; i--) {
/* std::cout << someVector[i]; ... */
}
Funciona, ya que los valores de tipos integrales sin signo se definen mediante el módulo de su conteo de bits. Por lo tanto, si está configurando -N
, termina en (2 ^ BIT_SIZE) -N
Usando matrices
Usando iteradores
Estamos usando std::reverse_iterator
para hacer la iteración.
for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a);
it != itb;
++it) {
/* std::cout << *it; .... */
}
Usando índices
Podemos usar de forma segura std::size_t
aquí, a diferencia de lo anterior, ya que sizeof
siempre devuelve std::size_t
por definición.
for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
/* std::cout << a[i]; ... */
}
Evitar las trampas con sizeof aplicado a punteros
En realidad, la forma anterior de determinar el tamaño de una matriz es una mierda. Si a es realmente un puntero en lugar de una matriz (lo cual sucede con bastante frecuencia, y los principiantes lo confundirán), fallará en silencio. Una mejor manera es usar lo siguiente, que fallará en tiempo de compilación, si se le da un puntero:
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];
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 tener sizeof
de: 1. Por lo tanto, la matriz devuelta tendrá un sizeof
de: N * 1, que es lo que estamos buscando, con solo evaluación de tiempo de compilación y sobrecarga cero de tiempo de ejecución.
En lugar de hacer
(sizeof a / sizeof *a)
Cambie su código para que ahora lo haga
(sizeof array_size(a))
En CI me gusta hacer esto:
int i = myArray.Length;
while (i--) {
myArray[i] = 42;
}
Ejemplo de C # agregado por MusiGenesis:
{int i = myArray.Length; while (i-- > 0)
{
myArray[i] = 42;
}}
Esa es definitivamente la mejor manera para cualquier matriz cuya longitud sea un tipo integral con signo. Para las matrices cuyas longitudes son un tipo integral sin signo (por ejemplo, un std::vector
en C ++), entonces necesita modificar ligeramente la condición final:
for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
// blah
Si acabas de decir i >= 0
, esto siempre es cierto para un entero sin signo, por lo que el ciclo será un ciclo infinito.
La mejor manera de hacerlo en C ++ es usar adaptadores de iterador (o mejor, rango), que transformarán la secuencia de forma perezosa a medida que se la atraviesa.
Básicamente,
vector<value_type> range;
foreach(value_type v, range | reversed)
cout << v;
Muestra el rango "rango" (aquí está vacío, pero estoy bastante seguro de que puede agregar elementos) en orden inverso. Por supuesto, simplemente iterar en el rango no es de mucha utilidad, pero pasar ese nuevo rango a los algoritmos y esas cosas es bastante bueno.
Este mecanismo también se puede usar para usos mucho más potentes:
range | transformed(f) | filtered(p) | reversed
Se calculará holgadamente el rango "rango", donde la función "f" se aplica a todos los elementos, los elementos para los cuales "p" no es verdadero se eliminan, y finalmente el rango resultante se invierte.
La sintaxis de Pipe es la OMI más legible, dado su infijo. La actualización pendiente de actualización de la biblioteca Boost.Range implementa esto, pero también es bastante sencillo hacerlo usted mismo. Es aún más genial con un DSEL lambda para generar la función fy el predicado p en línea.
Me parece bien. Si el indexador no está firmado (uint, etc.), es posible que deba tenerlo en cuenta. Llámame flojo, pero en ese caso (sin firmar), podría usar una contravariable:
uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
arr[--pos] = 42;
}
(en realidad, incluso aquí tendrías que 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!)
NOTA: Esta publicación terminó siendo mucho más detallada y, por lo tanto, no relacionada con el 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 (soy nuevo en el sitio).
De todos modos, esta es la versión de C # en .NET 3.5, que es increíble, ya que funciona en cualquier tipo de colección que utilice la semántica definida. Esta es una medida predeterminada (¡reutilización!) No rendimiento o 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 funciona sobre cualquier tipo de colección y toma un delegado de acción que espera un único valor del tipo, todo ejecutado sobre cada elemento en reversa **
Requres 3.5:
public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
{
foreach (var contextItem in sequenceToReverse.Reverse())
doForEachReversed(contextItem);
}
¿Versiones anteriores de .NET o quieres entender mejor las internas de Linq? Sigue leyendo .. O no ...
ASUNCIÓN: En el sistema de tipo .NET, el tipo de matriz 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 Matriz de tipo ''objeto'', cualquier tipo es válido,
MEDIDA CRÍTICA: Suponemos que si puede procesar cualquier secuencia en orden inverso que sea "mejor", solo podrá hacerlo en 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 contenga sea del mismo tipo. Entonces, si recibimos una matriz, toda la matriz contiene instancias de tipo X. Si alguna otra instancia es de un tipo! = X se lanza una excepción:
Un servicio singleton:
clase pública ReverserService {private ReverserService () {}
/// <summary>
/// Most importantly uses yield command for efficiency
/// </summary>
/// <param name="enumerableInstance"></param>
/// <returns></returns>
public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
{
if (enumerableInstance == null)
{
throw new ArgumentNullException("enumerableInstance");
}
// First we need to move forwarad and create a temp
// copy of a type that allows us to move backwards
// We can use ArrayList for this as the concrete
// type
IList reversedEnumerable = new ArrayList();
IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();
while (tempEnumerator.MoveNext())
{
reversedEnumerable.Add(tempEnumerator.Current);
}
// Now we do the standard reverse over this using yield to return
// the result
// NOTE: This is an immutable result by design. That is
// a design goal for this simple question as well as most other set related
// requirements, which is why Linq results are immutable for example
// In fact this is foundational code to understand Linq
for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
{
yield return reversedEnumerable[i];
}
}
}
public static class ExtensionMethods
{
public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
{
return ReverserService.ToReveresed(enumerableInstance);
}
}
[TestFixture] clase pública Testing123 {
/// <summary>
/// .NET 1.1 CLR
/// </summary>
[Test]
public void Tester_fornet_1_dot_1()
{
const int initialSize = 1000;
// Create the baseline data
int[] myArray = new int[initialSize];
for (var i = 0; i < initialSize; i++)
{
myArray[i] = i + 1;
}
IEnumerable _revered = ReverserService.ToReveresed(myArray);
Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
}
[Test]
public void tester_why_this_is_good()
{
ArrayList names = new ArrayList();
names.Add("Jim");
names.Add("Bob");
names.Add("Eric");
names.Add("Sam");
IEnumerable _revered = ReverserService.ToReveresed(names);
Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));
}
[Test]
public void tester_extension_method()
{
// Extension Methods No Linq (Linq does this for you as I will show)
var enumerableOfInt = Enumerable.Range(1, 1000);
// Use Extension Method - which simply wraps older clr code
IEnumerable _revered = enumerableOfInt.ToReveresed();
Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
}
[Test]
public void tester_linq_3_dot_5_clr()
{
// Extension Methods No Linq (Linq does this for you as I will show)
IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);
// Reverse is Linq (which is are extension methods off IEnumerable<T>
// Note you must case IEnumerable (non generic) using OfType or Cast
IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();
Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
}
[Test]
public void tester_final_and_recommended_colution()
{
var enumerableOfInt = Enumerable.Range(1, 1000);
enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));
}
private static object TestAndGetResult(IEnumerable enumerableIn)
{
// IEnumerable x = ReverserService.ToReveresed(names);
Assert.IsTrue(enumerableIn != null);
IEnumerator _test = enumerableIn.GetEnumerator();
// Move to first
Assert.IsTrue(_test.MoveNext());
return _test.Current;
}
}
No estoy seguro de ver por qué alguna de las alternativas es mejor, si la bondad incluye claridad o facilidad de mantenimiento.
Prefiero un ciclo while. Para mí es más claro que disminuir i
en la condición de un bucle for
int i = arrayLength;
while(i)
{
i--;
//do something with array[i]
}
Si bien es cierto que es un poco oscuro, diría que la forma más agradable de hacer esto es
for (int i = myArray.Length; i --> 0; )
{
//do something
}
Usaría el código en la pregunta original, pero si realmente quisiera usar foreach y tener un índice entero en C #:
foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
myArray[i] = 42;
}
Voy a intentar responder mi propia pregunta aquí, pero tampoco me gusta esto:
for (int i = 0; i < myArray.Length; i++)
{
int iBackwards = myArray.Length - 1 - i; // ugh
myArray[iBackwards] = 666;
}
Yo siempre preferiría un código claro contra el código '' tipográfico agradable ''. Por lo tanto, siempre usaría:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something ...
}
Puede considerarlo como la forma estándar de retroceder.
Solo mis dos centavos ...
En C # , usando Visual Studio 2005 o posterior, escriba ''forr'' y presione [TAB] [TAB] . Esto se ampliará a un bucle for
que irá hacia atrás a través de una colección.
Es tan fácil equivocarse (al menos para mí), que pensé que poner esta muestra sería una buena idea.
Dicho esto, me gusta Array.Reverse()
/ Enumerable.Reverse()
y luego iterar hacia delante mejor; ellos indican más claramente el intento.
// this is how I always do it
for (i = n; --i >= 0;){
...
}