Tengo el siguiente código:
if (this->_car.getAbsoluteAngle() <= 30 || this->_car.getAbsoluteAngle() >= 330)
this->_car.edir = Car::EDirection::RIGHT;
else if (this->_car.getAbsoluteAngle() > 30 && this->_car.getAbsoluteAngle() <= 60)
this->_car.edir = Car::EDirection::UP_RIGHT;
else if (this->_car.getAbsoluteAngle() > 60 && this->_car.getAbsoluteAngle() <= 120)
this->_car.edir = Car::EDirection::UP;
else if (this->_car.getAbsoluteAngle() > 120 && this->_car.getAbsoluteAngle() <= 150)
this->_car.edir = Car::EDirection::UP_LEFT;
else if (this->_car.getAbsoluteAngle() > 150 && this->_car.getAbsoluteAngle() <= 210)
this->_car.edir = Car::EDirection::LEFT;
else if (this->_car.getAbsoluteAngle() > 210 && this->_car.getAbsoluteAngle() <= 240)
this->_car.edir = Car::EDirection::DOWN_LEFT;
else if (this->_car.getAbsoluteAngle() > 240 && this->_car.getAbsoluteAngle() <= 300)
this->_car.edir = Car::EDirection::DOWN;
else if (this->_car.getAbsoluteAngle() > 300 && this->_car.getAbsoluteAngle() <= 330)
this->_car.edir = Car::EDirection::DOWN_RIGHT;
Quiero evitar la if
cadena s; es realmente feo. ¿Existe otra forma, posiblemente más limpia, de escribir esto?
c++
if-statement
Oraekia
fuente
fuente
this->_car.getAbsoluteAngle()
una vez antes de toda la cascada.this
(this->
) no es necesaria y realmente no hace nada bueno para la legibilidad ..>
pruebas; no son necesarios, ya que cada uno de ellos ya ha sido probado (en la dirección opuesta) en laif
declaración anterior .else if
.Respuestas:
Así es como lo haría yo. (Según mi comentario anterior).
fuente
q = a/b
yr = a%b
luegoq * b + r
deben ser igualesa
. Por lo tanto, es legal en C99 que un resto sea negativo. BorgLeader, puede solucionar el problema con(((angle % 360) + 360) % 360) / 30
.GetDirectionForAngle
es lo que propongo como reemplazo de la cascada if / else, ambos son O (1) ...Puede usar
map::lower_bound
y almacenar el límite superior de cada ángulo en un mapa.Ejemplo de trabajo a continuación:
fuente
table[angle%360/30]
respuestas porque es barata y no tiene ramas. Mucho más barato que un bucle de búsqueda de árbol, si se compila en un conjunto similar al de la fuente. (std::unordered_map
generalmente es una tabla hash, perostd::map
generalmente es un árbol binario rojo-negro. La respuesta aceptada se usa efectivamenteangle%360 / 30
como una función hash perfecta para ángulos (después de replicar un par de entradas, y la respuesta de Bijay incluso lo evita con un desplazamiento)).lower_bound
en una matriz ordenada. Eso sería mucho más eficiente que unmap
.this->_car.getAbsoluteAngle()
con un tmp var y eliminando la comparación redundante de cada una de lasif()
cláusulas del OP (verificando algo que ya hubiera coincidido el anterior if ()). O use la sugerencia de matriz ordenada de @ wilx.Cree una matriz, cada elemento del cual está asociado con un bloque de 30 grados:
Luego puede indexar la matriz con el ángulo / 30:
No se requieren comparaciones ni ramificaciones.
Sin embargo, el resultado es ligeramente diferente al original. Los valores en los bordes, es decir, 30, 60, 120, etc. se colocan en la siguiente categoría. Por ejemplo, en el código original los valores válidos para
UP_RIGHT
son 31 a 60. El código anterior asigna 30 a 59 aUP_RIGHT
.Podemos evitar esto restando 1 del ángulo:
Esto ahora nos da
RIGHT
30,UP_RIGHT
60, etc.En el caso de 0, la expresión se convierte en
(-1 % 360) / 30
. Esto es válido porque-1 % 360 == -1
y-1 / 30 == 0
, por lo que todavía obtenemos un índice de 0.La sección 5.6 del estándar C ++ confirma este comportamiento:
EDITAR:
Se plantearon muchas preguntas con respecto a la legibilidad y mantenibilidad de una construcción como esta. La respuesta dada por motoDrizzt es un buen ejemplo de cómo simplificar la construcción original que es más fácil de mantener y no es tan "fea".
Ampliando su respuesta, aquí hay otro ejemplo que utiliza el operador ternario. Dado que cada caso en la publicación original se asigna a la misma variable, el uso de este operador puede ayudar a aumentar aún más la legibilidad.
fuente
Ese código no es feo, es simple, práctico, legible y fácil de entender. Se aislará con su propio método, por lo que nadie tendrá que lidiar con él en la vida cotidiana. Y en caso de que alguien tenga que verificarlo, tal vez porque está depurando su aplicación por un problema en otro lugar, es tan fácil que le tomará dos segundos entender el código y lo que hace.
Si estuviera haciendo tal depuración, estaría feliz de no tener que pasar cinco minutos tratando de entender qué hace su función. En este sentido, todas las demás funciones fallan por completo, ya que cambian una rutina simple, olvidada y libre de errores, en un lío complicado que las personas al depurar se verán obligadas a analizar y probar en profundidad. Como gerente de proyecto, me molestaría mucho que un desarrollador tomara una tarea simple y, en lugar de implementarla de una manera simple e inofensiva, pierda el tiempo para implementarla de una manera demasiado complicada. Solo piense en todo el tiempo que perdió pensando en ello, luego viniendo a SO preguntando, y todo por el simple hecho de empeorar el mantenimiento y la legibilidad de la cosa.
Dicho esto, hay un error común en su código que lo hace bastante menos legible, y un par de mejoras que puede hacer con bastante facilidad:
Pon esto en un método, asigna el valor devuelto al objeto, contrae el método y olvídalo por el resto de la eternidad.
PD: hay otro error por encima del umbral de 330, pero no sé cómo quiere tratarlo, así que no lo solucioné en absoluto.
Actualización posterior
Según el comentario, incluso puede deshacerse del else si es que lo hace:
No lo hice porque siento que en cierto punto se convierte en una cuestión de preferencias propias, y el alcance de mi respuesta fue (y es) para dar una perspectiva diferente a su preocupación por la "fealdad del código". De todos modos, como dije, alguien lo señaló en los comentarios y creo que tiene sentido mostrarlo.
fuente
else if
,if
es suficiente.else if
. Encuentro útil poder echar un vistazo a un bloque de código y ver que es un árbol de decisiones, en lugar de un grupo de declaraciones no relacionadas. Sí,else
obreak
no son necesarios para el compilador después de areturn
, pero son útiles para el humano que está mirando el código.elseif
/ separadaelsif
, o técnicamente está usando un bloque de una declaración que comienzaif
, como aquí. Ejemplo rápido de lo que creo que estás pensando y en lo que estoy pensando: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532aelse
que te obliga a hacer eso, es una guía de estilo pobre que no reconoceelse if
como una declaración distinta. Siempre usaría llaves, pero nunca escribiría un código como ese, como mostré en mi esencia.En pseudocódigo:
Por lo tanto, tenemos
0-60
,60-90
,90-150
, ... como las categorías. En cada cuadrante con 90 grados, una parte tiene 60, una parte tiene 30. Entonces, ahora:Use el índice en una matriz que contenga las enumeraciones en el orden apropiado.
fuente
fuente
Ignorando el primero,
if
que es un caso un poco especial, los restantes siguen exactamente el mismo patrón: un mínimo, un máximo y una dirección; pseudocódigo:Hacer este C ++ real podría verse así:
Ahora, en lugar de escribir un montón de
if
s, simplemente recorra sus diversas posibilidades:(
throw
Ing una excepción más quereturn
INGNONE
es otra opción).Que luego llamarías:
Esta técnica se conoce como programación basada en datos . Además de deshacerse de un montón de
if
s, le permitiría agregar fácilmente más direcciones (por ejemplo, NNW) o reducir el número (izquierda, derecha, arriba, abajo) sin tener que volver a trabajar en otro código.(Manejar su primer caso especial se deja como "un ejercicio para el lector" :-))
fuente
if(angle <= angleRange.max)
+1 para usar características de C ++ 11 comoenum class
.Aunque las variantes propuestas basadas en una tabla de búsqueda para
angle / 30
son probablemente preferibles, aquí hay una alternativa que usa una búsqueda binaria codificada para minimizar el número de comparaciones.fuente
Si realmente desea evitar la duplicación, puede expresarlo como una fórmula matemática.
En primer lugar, suponga que estamos usando la enumeración de @ Geek
Ahora podemos calcular la enumeración usando matemáticas enteras (sin la necesidad de matrices).
Como señala @motoDrizzt, el código conciso no es necesariamente un código legible. Tiene la pequeña ventaja de que expresarlo como matemáticas hace explícito que algunas direcciones cubren un arco más amplio. Si desea ir en esta dirección, puede agregar algunas afirmaciones para ayudar a comprender el código.
Habiendo agregado las afirmaciones, ha agregado duplicación, pero la duplicación en las afirmaciones no es tan mala. Si tiene una afirmación inconsistente, lo sabrá muy pronto. Las afirmaciones se pueden compilar fuera de la versión de lanzamiento para no inflar el ejecutable que distribuye. Sin embargo, este enfoque probablemente sea más aplicable si desea optimizar el código en lugar de hacerlo menos feo.
fuente
Llego tarde a la fiesta, pero podríamos usar indicadores de enumeración y comprobaciones de rango para hacer algo ordenado.
la comprobación (pseudocódigo), asumiendo que el ángulo es absoluto (entre 0 y 360):
fuente