Nunca antes visto C ++ para loop

164

Estaba convirtiendo un algoritmo de C ++ a C #. Encontré esto para el bucle:

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;

No da ningún error en C ++, pero sí en C # (no puede convertir int a bool). Realmente no puedo entender esto para el bucle, ¿dónde está la condición?

¿Alguien puede explicar?

PD. Solo para verificar, para adaptar un VECTOR a una LISTA, ¿corresponde b.back () a b [b.Count-1]?

Thomas
fuente
35
¿Dónde está la condición? Eso seria u--. Los punto y coma se utilizan para delimitar las diversas partes de la fordeclaración.
David Heffernan
77
Este es un ciclo bastante normal. C # no convierte los números a bools implícitamente, por lo que debe convertir la condición en; u-- != 0;
R. Martinho Fernandes
28
@Jessie Good: un buen código no tiene nada que ver con lo que está permitido en el idioma, tiene que ver con cuánto tiempo le toma a un compañero de trabajo desconocido leer el código. Si causa algún tipo de confusión, no es la mejor solución posible, incluso si es legal. A menudo, una solución más detallada es mucho mejor que una breve, y la mayoría de los compiladores compilarán de la misma manera.
Bill K
18
Espero que, después de convertir el código, le das las variables mejores nombres que b, u, v, etc. La única razón por la que fueron nombrados de esta manera es porque alguien quería mirar inteligente al hacer su código ilegible.
Dan
33
@houbysoft: este es un problema general de stackoverflow. Si hace una pregunta muy detallada, bien investigada e interesante en un dominio de investigación específico, lo que lleva a una solución a un problema difícil e interesante, y responde a esa pregunta después de una dura jornada de investigación, obtendrá solo unos pocos docenas de visitantes y uno o dos votos positivos de algunos expertos en el campo. Si desea obtener muchas repeticiones rápidamente, debe hacer y responder preguntas como estas. "¿Cómo agrego dos números en php", "qué dosignifica en C ++"? Obtendrá miles de visitas de principiantes que buscan un tutorial.
vsz

Respuestas:

320

La condición del forbucle está en el medio, entre los dos puntos ;y comas .

En C ++ está bien poner casi cualquier expresión como condición: cualquier cosa que se evalúe como cero significa false; medios distintos de cero true.

En su caso, la condición es u--: cuando convierta a C #, simplemente agregue != 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE
dasblinkenlight
fuente
55
Por cierto, Thomas puede haber estado confundido por el uso de la coma también, es muy diferente al punto y coma, le permite hacer varias cosas en una sección del ciclo for (en este caso, inicializó dos variables). La última vez que verifiqué estas construcciones inusuales no se consideraban la solución más legible posible y, por lo tanto, algunos podrían estar mal vistas.
Bill K
2
Si no recuerdo mal, y la expresión que contiene una coma tiene el valor de la última subexpresión a la derecha. Esto viene de C y se puede usar en cualquier expresión, no solo en un bucle for.
Giorgio
77
@Roger Este no sería el mismo ciclo ya que está decrementando u al final del ciclo, en lugar del principio (es decir, después de b [u] = v en lugar de antes). De hecho, deberías inicializarlo en su u = b.size() - 1lugar.
Didier L
Fyi: Acabo de empujarte por encima del límite de 500K. ¿Es eso como ser el cliente número un millón en algún lugar y ganar algo? En cualquier caso: muchas felicidades; siempre admirando tus respuestas precisas y agudas! Sigue adelante; Nos vemos haciendo la cosa del millón ... más tarde este siglo.
GhostCat
165

Muchas respuestas precisas, pero creo que vale la pena escribir el bucle while equivalente.

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Es equivalente a:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}

Puede considerar refactorizar al formato while () a medida que traduce a C #. En mi opinión, es más claro, menos trampa para los nuevos programadores e igualmente eficiente.

Como otros han señalado - pero para que mi respuesta completa - para que funcione en C # que se necesita para el cambio while(u--)a while(u-- != 0).

... o while(u-- >0)solo en caso de que comience negativo. (OK, b.size()nunca será negativo, pero considere un caso general donde quizás algo más lo inicializó).

O, para hacerlo aún más claro:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

Es mejor ser claro que ser conciso.

Delgado
fuente
29
Esta respuesta no solo aclara el código incorrecto, sino que también ofrece una buena alternativa. ¡¡1 de ventaja!!
polvoazul
2
Como comentario aparte, tendría cuidado con la while (u-- >0)forma. Si el espacio se desordena, puede terminar con un bucle "a cero" while (u --> 0), que tiende a confundir a todos a primera vista. ( No estoy seguro de si es válido C #, pero está en C, y creo que podría estarlo también en C ++ ).
Izkata
9
No creo que tu código sea necesariamente más claro. El punto de, en forlugar de, whilees precisamente que pones la inicialización y el incremento / decremento en una declaración, y eso no necesariamente hace que el código sea más difícil de entender. De lo contrario, no deberíamos estar usando foren absoluto.
musiphil
1
También es importante reescribir el menor código posible. (El bucle for podría cambiar, después de todo.) Ha puesto "u--" en dos lugares separados, y el bucle no es realmente más claro (puedo ver lo que hace el bucle for de un vistazo; tengo que escanear con múltiples líneas). Ser conciso también tiene beneficios. No los minimices. Aún así, un buen ejemplo de cómo se podría escribir. Hace que sea más fácil de entender para cualquiera que no esté acostumbrado a las declaraciones en C ++ (o tal vez incluso en absoluto).
NotKyon
2
@Izkata: NUNCA es un operador, se analiza como un --token seguido de un >token. Dos operadores separados. Un ciclo de "bajar a cero" es solo una combinación directa de post-decremento y mayor que. La sobrecarga de operadores de C ++ no crea nuevos operadores, solo reutiliza los existentes.
Ben Voigt
66

La condición es u--;, porque está en la segunda posición de la de la instrucción.

Si el valor de u--;es diferente de 0, se interpretará como true(es decir, se convierte implícitamente en el valor booleano true). Si, en cambio, su valor es 0, se convertirá en false.

Este es un código muy malo .

Actualización: Discutí la escritura de bucles "for" en esta publicación de blog . Sus recomendaciones se pueden resumir en los siguientes párrafos:

Un bucle for es una construcción práctica, legible (una vez que te acostumbras) y concisa, pero debes usarla bien. Debido a su sintaxis poco común, usarlo de una manera demasiado imaginativa no es una buena idea.

Todas las partes del bucle for deben ser cortas y legibles. Se deben elegir nombres de variables para que sea fácil de entender.

Este ejemplo viola claramente estas recomendaciones.

Daniel Daranas
fuente
3
¿O se detendrá en u == 0 probablemente ...?
Thomas
2
No; en C ++ hay una conversión implícita de int a bool, con 0 convirtiendo a falso. El ciclo finalizará cuando u-- == 0. En C #, no existe tal conversión implícita, por lo que tendría que decir explícitamente u-- == 0. EDITAR: Esto es en respuesta a su primer comentario.
Chris
48
Este es un código terrible , por una razón muy simple; no podías comprenderlo fácilmente cuando lo leías. Es "inteligente", un "hack"; hace uso de una combinación de estructuras de codificación y conocimiento de cómo funcionan detrás de escena, para crear una estructura que haga el trabajo pero que desafíe la comprensión, porque no está en la forma que los autores del idioma imaginaron y que se comunicó a la mayoría usuarios de idiomas.
KeithS
44
Excelente respuesta Lo haría +1 ... si no el "Este es un código muy malo". declaración;)
Sandman4
14
No entiendo por qué tantas personas parecen pensar U-- es muy mal código simplemente debido a la falta (implícita en C ++) ! = 0 . Seguramente cualquiera que trabaje con código sabrá perfectamente que 0 = falso, cualquier otro valor = verdadero . Hay mucho más posibilidades de confusión con respecto a / pre-post-incremento de U , o tal vez la gente asumiendo que u = B.Size () será siempre ejecutar antes de v = b.back () (Mi entendimiento es la secuencia de ejecución no está definido, pero estoy para ser corregido).
FumbleFingers
23

Esta será la forma C # de su ciclo.

// back fetches the last element of vector in c++.
for (u = b.size(), v = b.back(); (u--) != 0; v = p[v]) 
{      
  b[u] = v;      
}

Simplemente reemplace el equivalente para size () y back ().

Lo que hace es invertir la lista y almacenar en una matriz. Pero en C # tenemos directamente una función definida por el sistema para esto. Por lo tanto, no necesita escribir este bucle también.

b = b.Reverse().ToArray();
Narendra
fuente
1
mediante la adopción v = b.back();de la intializer para no acabas de cambiar la forma en que funciona, ya que el v = p[v]que se sustituye con
João Portela
v = p [v] no tendrá ningún efecto en el resultado final ya que esta línea se ejecutará en el último. Y después de eso v no se refiere en el bucle. Esta línea está ahí solo para mostrar cómo se convierte el bucle de c ++ a C #.
Narendra
1
en el código c ++ v = b.back();se ejecutó una vez antes de que comenzaran las iteraciones y v = p[v]se ejecutó al comienzo de cada iteración. En esa versión de C # v = p[v]todavía se ejecuta al comienzo de cada iteración, pero v = b.back();se ejecuta inmediatamente después, cambiando el valor de vla siguiente instrucción b[u] = v;. (tal vez la pregunta fue editada después de leerla)
João Portela
55
@Rain El problema es v = b.back(). Lo tiene ejecutándose en cada iteración del bucle en lugar de solo la primera: no sabemos qué back()hace (¿hay algún efecto secundario? ¿Cambia la representación interna de b?), Por lo que este bucle no es equivalente al de la pregunta.
Izkata
1
@ Lluvia Exactamente, échale un vistazo más de cerca a ti mismo. El paso de inicialización ocurre solo una vez antes de que comience el ciclo, no al comienzo de cada iteración . Su código sería correcto si v = b.back()se moviera fuera del bucle, por encima de él. (Además, si está tratando de responder a alguien, @
úselo
15
u = b.size(), v = b.back()

Es la inicialización.

u--

Es la condición.

v = p[v]

es la iteración

huseyin tugrul buyukisik
fuente
14

La condición es el resultado de u--, que es el valor de uantes de que fuera decrementado.

En C y C ++, an int es convertible a bool haciendo una != 0comparación implícita (0 es false, todo lo demás es true).

b.back()es el último elemento en un contenedor, que es b[b.size() - 1]cuándo size() != 0.

Bo Persson
fuente
11

En C, todo lo que no sea cero está trueen contextos "booleanos", como la condición de finalización del ciclo o una declaración condicional. En C # que tiene que hacer esa comprobación explícita: u-- != 0.

Joey
fuente
Esta no es la cuestión de OP. Él está preguntando sobre la evaluación de la condición terminal (es decir, 'u--' en este caso).
ApplePie
6

Como han dicho otros, el hecho de que C ++ tenga conversión implícita a booleana significa que el condicional es u--, lo que será cierto si el valor no es cero.

Vale la pena agregar, que tienes una suposición falsa al preguntar "dónde está el condicional". En C ++ y C # (y otros lenguajes sintaxis similares) puede tener un condicional vacío. En este caso siempre se evalúa como verdadera, por lo que el bucle continúa para siempre, o hasta que alguna otra condición que las salidas (a través de return, breako throw).

for(int i = 0; ; ++i)
  doThisForever(i);

De hecho, cualquier parte de la declaración for puede omitirse, en cuyo caso simplemente no se realiza.

En general, for(A; B; C){D}o se for(A; B; C)D;convierte en:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:

Cualquiera o más de A, B, C o D pueden quedar fuera.

Como resultado de esto, algunos favorecen los for(;;) bucles infinitos. Lo hago porque, aunque while(true)es más popular, lo leí como "hasta que la verdad deje de ser verdad", lo que suena algo apocalíptico en comparación con mi lectura for(;;) como "para siempre".

Es una cuestión de gustos, pero como no soy la única persona en el mundo a la que le gusta for(;;), vale la pena saber lo que significa.

Jon Hanna
fuente
4

todas las respuestas son correctas:

for loop se puede usar de varias maneras de la siguiente manera:

Single Statement inside For Loop
Multiple Statements inside For Loop
No Statement inside For Loop
Semicolon at the end of For Loop
Multiple Initialization Statement inside For
Missing Initialization in For Loop
Missing Increment/Decrement Statement
Infinite For Loop
Condition with no Conditional Operator.
Birubisht
fuente
4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

En el código anterior, uy vse inicializan con b.size()y b.back().

Cada vez que se verifica la condición, también se ejecuta la declaración de decremento, es decir u--.

El forbucle saldrá cuando use convierta 0.

Apte
fuente
3

El error encontrado en C # en sí borra la duda. El bucle for busca un

FALSO

condición para terminar. Y como sabemos

(BOOL) FALSE = (int) 0

pero C # no puede procesar esto por sí solo a diferencia de C ++. Entonces, la condición que estás buscando es

tu--

pero tienes que dar explícitamente la condición en C # como

u--! = 0

o

u--> 0

Pero aún así trate de evitar este tipo de práctica de codificación. los

mientras bucle

mencionado anteriormente en la respuesta es una de las versiones más simplificadas de su

en bucle.

Abhineet
fuente
@Downvoter: Downvoting está bien siempre que no esté satisfecho con la solución, pero al mismo tiempo, tómese el tiempo para indicar el motivo, de modo que las respuestas puedan mejorarse.
Abhineet
3

Si está acostumbrado a C / C ++, este código no es tan difícil de leer, aunque es bastante conciso y no es tan bueno de código. Permítanme explicar las partes que son más Cism que cualquier otra cosa. Primero, la sintaxis general de un bucle C for se ve así:

for (<initialization> ; <condition>; <increment>)
{
    <code...>
}

El código de inicialización se ejecuta una vez. Luego, la condición se prueba antes de cada ciclo y, finalmente, se llama al incremento después de cada ciclo. Entonces, en su ejemplo, encontrará que la condición esu--

¿Por qué u--funciona como una condición en C y no en C #? Debido a que C convierte implícitamente muchas cosas demasiado, esto puede causar problemas. Para un número, todo lo que no sea cero es verdadero y cero es falso. Por lo tanto, contará desde b.size () - 1 a 0. Tener el efecto secundario en la condición es un poco molesto y sería preferible colocarlo en la parte de incremento del ciclo for, aunque mucha C El código hace esto. Si lo estuviera escribiendo lo haría más así:

for (u = b.size() - 1, v = b.back(); u>=0; --u) 
{
    b[u] = v;
    v = p[v]
}

La razón de esto es, al menos para mí, es más claro. Cada parte del bucle for hace su trabajo y nada más. En el código original, la condición estaba modificando la variable. La parte de incremento estaba haciendo algo que debería estar en el bloque de código, etc.

El operador de coma también puede estar lanzándole un bucle. En C, algo así x=1,y=2parece una declaración en lo que respecta al compilador y se ajusta al código de inicialización. Simplemente evalúa cada una de las partes y devuelve el valor de la última. Así por ejemplo:

std::cout << "(1,2)=" << (1,2) << std::endl;

imprimiría 2.

Matt Price
fuente
El problema con su reescritura es que si b.size () no está firmado, iterará durante mucho tiempo. (Además, le falta un punto y coma.) Pero al menos no adoptó el enfoque "'Dick and Jane' es buen inglés" que muchas de las otras respuestas y comentarios hicieron, elogiando reescrituras absurdamente verbosas que solo son más fáciles para leer por neófitos y otros programadores no calificados.
Jim Balter