size_tno está firmado, por lo que se garantiza que se ajustará a su valor máximo cuando se intente desaprobar el cero, finalizando el ciclo. Sin embargo, sigue siendo un código horrible.
BoBTFish
4
@Bathsheba ¿Qué Mpasa si es el valor máximo para size_t? ¿Sigues pensando que es inteligente?
Thorsten Dittmar
8
@Barmar No surge mucho, no Nunca surge , por lo que hay una probabilidad> 0. Lo que significa que esta solución es estúpida cuando hay formas de ponerlo simple.
Thorsten Dittmar
38
No entiendo las votaciones negativas. Esta pregunta es clara: no veo cómo se podría mejorar. De un nuevo usuario, ¡esta pregunta es realmente interesante !
Betsabé
17
#define TRUE FALSEy vete de vacaciones.
Leo Heinsaar
Respuestas:
68
std::size_testá garantizado por el estándar C ++ para ser un unsignedtipo. Y si disminuye un unsignedtipo desde 0, el estándar garantiza que el resultado de hacerlo es el valor más grande para ese tipo.
Ese valor envuelto siempre es mayor o igual a M1, por lo que el ciclo termina.
Entonces, j <= Mcuando se aplica a un unsignedtipo, es una forma conveniente de decir "ejecutar el ciclo a cero y luego detenerse".
Existen alternativas como ejecutar juno más grande de lo que desea, e incluso usar el operador de diapositivafor (std::size_t j = M + 1; j --> 0; ){ , que posiblemente son más claras, aunque requieren más escritura. Sin embargo, supongo que una desventaja (aparte del efecto desconcertante que produce en la primera inspección) es que no se adapta bien a lenguajes sin tipos sin firmar, como Java.
Tenga en cuenta también que el esquema que ha elegido su jefe "toma prestado" un valor posible del unsignedconjunto: en este caso sucede que el Mconjunto a std::numeric_limits<std::size_t>::max()no tendrá el comportamiento correcto. De hecho, en ese caso, el bucle es infinito . (¿Es eso lo que está observando?) Debería insertar un comentario en ese sentido en el código, y posiblemente incluso afirmar sobre esa condición en particular.
1 Sujeto a Mno ser std::numeric_limits<std::size_t>::max().
Si M es, std::numeric_limits<std::size_t>::max()entonces M + 1será cero y el for (std::size_t j = M + 1; j --> 0; )bucle no formará bucle.
Pete Kirkham
51
No lo llames el "operador de diapositivas". No existe tal operador en C ++, es solo una ofuscación de aspecto inteligente.
Usted
26
@DimitarMirchev: Porque no es un operador. Son dos operadores, con espacios impares. Llámelo un modismo si insiste en hacer preguntas irrelevantes a los entrevistados (pero, idealmente, pregúnteles cómo implementarían la funcionalidad en lugar de preguntar sobre la sintaxis "inteligente").
Usted
1
Suponiendo que size_tsea de 64 bits, se necesitarán varios cientos de años para observar el comportamiento incorrecto en el caso de borde. (Excepto si el optimizador puede deshacerse del bucle).
Carsten S
27
Lo que su jefe probablemente estaba tratando de hacer era contar hacia atrás desde Mcero inclusive, realizando alguna acción en cada número.
Por desgracia hay un caso extremo en que habrá de hecho le dará un bucle infinito, aquella en la que Mes el máximo size_tvalor que puede tener. Y, aunque está bien definido lo que hará un valor sin firmar cuando lo disminuyas desde cero, mantengo que el código en sí es un ejemplo de pensamiento descuidado, especialmente porque hay una solución perfectamente viable sin las deficiencias del intento de tus jefes.
Esa variante más segura (y más legible, en mi opinión, sin dejar de mantener un límite de alcance ajustado), sería:
{
std::size_t j = M;
do {
doSomethingWith(j);
} while (j-- != 0);
}
A modo de ejemplo, consulte el siguiente código:
#include<iostream>#include<cstdint>#include<climits>intmain(void){
uint32_t quant = 0;
unsignedshort us = USHRT_MAX;
std::cout << "Starting at " << us;
do {
quant++;
} while (us-- != 0);
std::cout << ", we would loop " << quant << " times.\n";
return0;
}
Esto hace básicamente lo mismo con un unsigned shorty puede ver que procesa todos los valores:
Starting at 65535, we would loop 65536 times.
Reemplazar el do..whileciclo en el código anterior con lo que hizo básicamente su jefe resultará en un ciclo infinito. Pruébelo y vea:
Ahora, el caso de borde es 0. ¿Por qué no for(size_t j = M; j-- != 0; )?
LogicStuff
Sí, esto sufre de que el caso de la esquina sea quizás más frecuente que numeric_limits<size_t>::max().
Betsabé
7
@Logic et al, no hay caso de borde en el método que proporcioné, he agregado código para mostrar esto. Con su solución propuesta , un valor inicial de M == 0dará como resultado que el elemento 0 no se procese, por lo que tiene un caso de borde. El uso de mi do..whilemétodo de verificación posterior elimina el caso de borde por completo. Si lo prueba con M == 1, verá que hace 1 y 0. De manera similar, comience con max_size_t(lo que sea que sea) y comenzará con éxito en ese punto y luego disminuirá hasta cero inclusive.
paxdiablo
Curiosamente, este caso motiva el uso de lo que algunos dirían que es “código no estructurado” porque usa un “ exitif”, es decir { j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }. Incluso podría ser necesario reemplazar el breakcon a gotosi las cosas se anidan, ya que C no tiene bucles con nombre. Si "estructurado" significa "susceptible de razonar sobre fragmentos", está estructurado (¡el diseño ayuda!), Y también si significa "susceptible de validación formal mediante el razonamiento sobre condiciones previas y posteriores". Aunque en este caso j--funciona, el estilo exitif puede estar justificado cuando se requiere una transición más complicada.
PJTraill
@PJTraill No puedo decir que lo que estás diciendo está estructurado y lo que estás diciendo no está estructurado. Razonar sobre el do-while es sencillo. No "usa" una salida más de lo que lo hace while-do, o de una manera no estructurada.
size_t
no está firmado, por lo que se garantiza que se ajustará a su valor máximo cuando se intente desaprobar el cero, finalizando el ciclo. Sin embargo, sigue siendo un código horrible.M
pasa si es el valor máximo parasize_t
? ¿Sigues pensando que es inteligente?#define TRUE FALSE
y vete de vacaciones.Respuestas:
std::size_t
está garantizado por el estándar C ++ para ser ununsigned
tipo. Y si disminuye ununsigned
tipo desde 0, el estándar garantiza que el resultado de hacerlo es el valor más grande para ese tipo.Ese valor envuelto siempre es mayor o igual a
M
1, por lo que el ciclo termina.Entonces,
j <= M
cuando se aplica a ununsigned
tipo, es una forma conveniente de decir "ejecutar el ciclo a cero y luego detenerse".Existen alternativas como ejecutar
j
uno más grande de lo que desea, e incluso usar el operador de diapositivafor (std::size_t j = M + 1; j --> 0; ){
, que posiblemente son más claras, aunque requieren más escritura. Sin embargo, supongo que una desventaja (aparte del efecto desconcertante que produce en la primera inspección) es que no se adapta bien a lenguajes sin tipos sin firmar, como Java.Tenga en cuenta también que el esquema que ha elegido su jefe "toma prestado" un valor posible del
unsigned
conjunto: en este caso sucede que elM
conjunto astd::numeric_limits<std::size_t>::max()
no tendrá el comportamiento correcto. De hecho, en ese caso, el bucle es infinito . (¿Es eso lo que está observando?) Debería insertar un comentario en ese sentido en el código, y posiblemente incluso afirmar sobre esa condición en particular.1 Sujeto a
M
no serstd::numeric_limits<std::size_t>::max()
.fuente
std::numeric_limits<std::size_t>::max()
entoncesM + 1
será cero y elfor (std::size_t j = M + 1; j --> 0; )
bucle no formará bucle.size_t
sea de 64 bits, se necesitarán varios cientos de años para observar el comportamiento incorrecto en el caso de borde. (Excepto si el optimizador puede deshacerse del bucle).Lo que su jefe probablemente estaba tratando de hacer era contar hacia atrás desde
M
cero inclusive, realizando alguna acción en cada número.Por desgracia hay un caso extremo en que habrá de hecho le dará un bucle infinito, aquella en la que
M
es el máximosize_t
valor que puede tener. Y, aunque está bien definido lo que hará un valor sin firmar cuando lo disminuyas desde cero, mantengo que el código en sí es un ejemplo de pensamiento descuidado, especialmente porque hay una solución perfectamente viable sin las deficiencias del intento de tus jefes.Esa variante más segura (y más legible, en mi opinión, sin dejar de mantener un límite de alcance ajustado), sería:
{ std::size_t j = M; do { doSomethingWith(j); } while (j-- != 0); }
A modo de ejemplo, consulte el siguiente código:
#include <iostream> #include <cstdint> #include <climits> int main (void) { uint32_t quant = 0; unsigned short us = USHRT_MAX; std::cout << "Starting at " << us; do { quant++; } while (us-- != 0); std::cout << ", we would loop " << quant << " times.\n"; return 0; }
Esto hace básicamente lo mismo con un
unsigned short
y puede ver que procesa todos los valores:Starting at 65535, we would loop 65536 times.
Reemplazar el
do..while
ciclo en el código anterior con lo que hizo básicamente su jefe resultará en un ciclo infinito. Pruébelo y vea:for (unsigned int us2 = us; us2 <= us; --us2) { quant++; }
fuente
0
. ¿Por qué nofor(size_t j = M; j-- != 0; )
?numeric_limits<size_t>::max()
.M == 0
dará como resultado que el elemento 0 no se procese, por lo que tiene un caso de borde. El uso de mido..while
método de verificación posterior elimina el caso de borde por completo. Si lo prueba conM == 1
, verá que hace 1 y 0. De manera similar, comience conmax_size_t
(lo que sea que sea) y comenzará con éxito en ese punto y luego disminuirá hasta cero inclusive.exitif
”, es decir{ j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }
. Incluso podría ser necesario reemplazar elbreak
con agoto
si las cosas se anidan, ya que C no tiene bucles con nombre. Si "estructurado" significa "susceptible de razonar sobre fragmentos", está estructurado (¡el diseño ayuda!), Y también si significa "susceptible de validación formal mediante el razonamiento sobre condiciones previas y posteriores". Aunque en este casoj--
funciona, el estilo exitif puede estar justificado cuando se requiere una transición más complicada.