En muchos idiomas, las asignaciones son legales en condiciones. Nunca entendí la razón detrás de esto. Por qué escribirías:
if (var1 = var2) {
...
}
en vez de:
var1 = var2;
if (var1) {
...
}
En muchos idiomas, las asignaciones son legales en condiciones. Nunca entendí la razón detrás de esto. Por qué escribirías:
if (var1 = var2) {
...
}
en vez de:
var1 = var2;
if (var1) {
...
}
Respuestas:
Es más útil para bucles que declaraciones if.
while( var = GetNext() )
{
...do something with var
}
Que de otro modo tendría que escribirse
var = GetNext();
while( var )
{
...do something
var = GetNext();
}
Lo encuentro más útil en cadenas de acciones que a menudo implican detección de errores, etc.
if ((rc = first_check(arg1, arg2)) != 0)
{
report error based on rc
}
else if ((rc = second_check(arg2, arg3)) != 0)
{
report error based on new rc
}
else if ((rc = third_check(arg3, arg4)) != 0)
{
report error based on new rc
}
else
{
do what you really wanted to do
}
La alternativa (no usar la asignación en la condición) es:
rc = first_check(arg1, arg2);
if (rc != 0)
{
report error based on rc
}
else
{
rc = second_check(arg2, arg3);
if (rc != 0)
{
report error based on new rc
}
else
{
rc = third_check(arg3, arg4);
if (rc != 0)
{
report error based on new rc
}
else
{
do what you really wanted to do
}
}
}
Con una verificación de errores prolongada, la alternativa puede salir del lado derecho de la página, mientras que la versión de asignación en condicional no lo hace.
Las comprobaciones de errores podrían ser también 'acciones' - first_action()
, second_action()
, third_action()
- por supuesto, en lugar de cheques solo. Es decir, se podrían comprobar pasos en el proceso que está gestionando la función. (La mayoría de las veces en el código con el que trabajo, las funciones están en la línea de las verificaciones de condiciones previas, o las asignaciones de memoria necesarias para que la función funcione, o en líneas similares).
Es más útil si está llamando a una función:
if (n = foo())
{
/* foo returned a non-zero value, do something with the return value */
} else {
/* foo returned zero, do something else */
}
Claro, puede poner n = foo (); en una declaración separada, entonces si (n), pero creo que lo anterior es un idioma bastante legible.
Puede ser útil si está llamando a una función que devuelve datos sobre los que trabajar o una marca para indicar un error (o que ya ha terminado).
Algo como:
while ((c = getchar()) != EOF) {
// process the character
}
// end of file reached...
Personalmente es un idioma que no me gusta mucho, pero a veces la alternativa es más fea.
GCC puede ayudarlo a detectar (con -Wall) si involuntariamente intenta usar una asignación como valor de verdad, en caso de que le recomiende escribir
if ((n = foo())) {
...
}
Es decir, use paréntesis extra para indicar que esto es realmente lo que desea.
El idioma es más útil cuando estás escribiendo un while
bucle en lugar de una if
declaración. Para una if
declaración, puede dividirlo como lo describe. Pero sin esta construcción, tendría que repetirse:
c = getchar();
while (c != EOF) {
// ...
c = getchar();
}
o use una estructura de bucle y medio:
while (true) {
c = getchar();
if (c == EOF) break;
// ...
}
Por lo general, preferiría la forma de bucle y medio.
while
bucle, prefiera usar:do { c = getchar(); ... } while (c != EOF);
for
se pueden usar bucles en su lugar: for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }
- este es el más conciso, y for
siempre me dice 'bucle', estilísticamente. El pequeño inconveniente es que debe especificar la función que regresa c
dos veces.
La respuesta corta es que los lenguajes de programación orientados a expresiones permiten un código más sucinto. No te obligan a separar los comandos de las consultas .
En PHP, por ejemplo, es útil para recorrer los resultados de la base de datos SQL:
while ($row = mysql_fetch_assoc($result)) {
// Display row
}
Esto se ve mucho mejor que:
$row = mysql_fetch_assoc($result);
while ($row) {
// Display row
$row = mysql_fetch_assoc($result);
}
La otra ventaja viene durante el uso de gdb. En el siguiente código no se conoce el código de error si fuéramos a un solo paso.
while (checkstatus() != -1) {
// process
}
Más bien
while (true) {
int error = checkstatus();
if (error != -1)
// process
else
//fail
}
Ahora, durante un solo paso, podemos saber cuál fue el código de error de retorno del checkstatus ().
Lo encuentro muy útil con funciones que devuelven opcionales ( boost::optional
o std::optional
en C ++ 17):
std::optional<int> maybe_int(); // function maybe returns an int
if (auto i = maybe_int()) {
use_int(*i);
}
Esto reduce el alcance de mi variable, hace que el código sea más compacto y no dificulta la legibilidad (lo encuentro).
Lo mismo con los punteros:
int* ptr_int();
if (int* i = ptr_int()) {
use_int(*i);
}
La razón es:
Mejora del rendimiento (a veces)
Menos código (siempre)
Tome un ejemplo: hay un método someMethod()
y en una if
condición desea verificar si el valor de retorno del método es null
. Si no es así, volverá a utilizar el valor de retorno.
If(null != someMethod()){
String s = someMethod();
......
//Use s
}
Obstaculizará el rendimiento ya que está llamando al mismo método dos veces. En su lugar use:
String s;
If(null != (s = someMethod())) {
......
//Use s
}