¿Cómo puedo reformatear mi condición para mejorarla?

35

tengo una condición

if(exists && !isDirectory || !exists)
{}

¿Cómo puedo modificarlo para que sea más comprensible?

Spynet
fuente
1
¿Qué valor tiene isDirectory cuando existe es falso?
marktani
1
existe es tipo Bool, isDirectory también es tipo BOOL variables
Spynet
55
si (! isDirectory) ... (existe ||! existe) siempre será verdadero.
Tiburón
@Shark - ¿Qué pasa si existsy isDirectoryson ambas verdaderas?
pasawaya
Leí el título como "Tengo un problema de personalidad, puedo borrar mi mente para solucionarlo". Sí, estoy cansada
Annan

Respuestas:

110

|| es conmutativo así

if(!exists || (exists && !isDirectory))

es equivalente.

Ahora porque existe siempre es cierto en la segunda parte de la ||que puede soltar &&:

if(!exists || !isDirectory)

O puede ir un paso más allá y hacer:

if(!(exists && isDirectory))
monstruo de trinquete
fuente
55
Lo que estaba implícito pero no se menciona explícitamente aquí es que &&tiene mayor prioridad (al menos en la mayoría de los idiomas conocidos, puede haber excepciones) que ||. Por a && b || clo tanto, es equivalente (a && b) || cpero no a a && (b || c).
Péter Török
27
Creo que eso !exists || !isDirectoryes más "comprensible", porque isDirectoryno puede ser cierto si !exists. Entonces, como humanos diremos "si no existe o si [existe y no] es un directorio".
duros
66
¡Prefiero! Existe || ! isDirectory sobre el último.
Apoorv Khurasia
3
||solo es conmutativo si se usa en valores sin efectos secundarios; si, por ejemplo, se usa con funciones, algunas funciones podrían no llamarse (cortocircuito) o devolver un valor diferente en un orden diferente.
orlp
26
Cualquiera que confíe en la precedencia relativa de '&&', '||', '==', '! =', Etc. y no aclare su intención al usar corchetes merece ser disparado. En todos los idiomas, algo como 'a && b || c 'es equivalente a un comentario que dice que el autor probablemente arruinó todo el asunto en su apuro por evitar escribir algunos caracteres adicionales.
Brendan
51

Como proceso, sugiero construir una tabla de verdad:

e = exists
d = isDirectory

e | d | (e && !d) || !e
--+---+----------------
0 | 0 | 1
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0

Esto coincide con la NANDoperación , que es simplemente:

!(exists && isDirectory)

Si no recuerda todas sus puertas lógicas, wikipedia tiene una buena referencia con las tablas de verdad para arrancar .


@Christoffer Hammarström planteó un punto importante sobre el estado de isDirectoryestar vinculado al estado de exists. Suponiendo que se refieren a la misma referencia, y que no es posible tener un estado donde la referencia no existe y es un directorio, la tabla de verdad se puede escribir de la siguiente manera:

e | d | (e && !d) || !e
--+---+----------------
0 | 0 | 1
0 | 1 | n/a
1 | 0 | 1
1 | 1 | 0

El n/ase utiliza para representar un estado que no importa. Reducciones aceptables podrían resultar en 1o 0para los estados resultantes en n/a.

Con esto en mente, !(exists && isDirectory)sigue siendo una reducción válida, lo que resulta en un 1for !e && d.

Sin embargo, !isDirectorysería una reducción mucho más simple, lo que resulta en 0para !e && d.

zzzzBov
fuente
44
El siguiente paso es darse cuenta de que isDirectorydepende exists. No puede ser un directorio y no existir.
Christoffer Hammarström
@ChristofferHammarstrom, fuera de contexto No puedo asumir que las variables se refieren a lo mismo, pero ese es un punto válido. La columna de resultados debe rellenarse n/aen lugares donde el estado es imposible de alcanzar, y la ecuación debe reducirse en consecuencia.
zzzzBov
Bueno, si las variables se refieren a dos contextos diferentes, entonces son demasiado concisas y necesitan ser renombradas.
Christoffer Hammarström
¡Pero construir una tabla de verdad y evaluarla es NP-complete!
Thomas Eding
@ThomasEding, tengo dos citas para ti: "En teoría, la teoría y la práctica son lo mismo; en la práctica, no lo son". y "La optimización prematura es la raíz de todo mal".
zzzzBov
22

Para una mejor legibilidad, me gusta extraer condiciones booleanas a los métodos:

if(fileNameUnused())
{...}

public boolean fileNameUnused() {
   return exists && !isDirectory || !exists;
}

O con un mejor nombre de método. Si puede nombrar este método correctamente, el lector de su código no necesita descubrir qué significa la condición booleana.

Puckl
fuente
+1 por decir algo sobre nombres útiles. Pero en algún lugar tendrá que reformatear el condicional.
Apoorv Khurasia
44
Una alternativa menos extrema, que aún transmite intención, es solo nombrar la condición utilizada:boolean fileNameUnused = !exists || !isDirectory; if (fileNameUnused) { doSomething(); }
Steven
8

Podrías tratar de arreglar el caso de no ir y rescatar si eso aparece.

while(someCondition) {

    if(exists && isDirectory)
        continue;
        // maybe "break", depends on what you're after.

        // the rest of the code
}

o incluso

function processFile(someFile)
{ 
    // ...
    if(exists && isDirectory)
       return false;
    // the rest of the code
    // ...
}
ZJR
fuente
¿No se consideran interrupciones, continúan y más de una declaración de devolución considerada olores de código?
Freiheit
8
@Freiheit Depende del contexto. A veces, se utiliza una declaración de devolución anticipada para reducir la sangría, lo que mejora la legibilidad.
marco-fiset
La mejor respuesta: los condicionales complejos desperdician enormes cantidades de tiempo leyendo y comprendiéndolos con precisión. Como resultado, a menudo se "toman como leídos", lo que genera errores insidiosos.
mattnz
6

Puede usar una tabla de verdad como se señaló. El segundo paso podría ser un mapa KV para minimizar el número de términos.

Usar las leyes del álgebra booleana es otro enfoque:

A = existe
B =! IsDirectory
! A =! Existe

&& = *
|| = +

[Editar]
Una transformación más simple, porque las operaciones AND y OR son mutuamente distributivas:

existe &&! isDirectory || ! existe
= A * B +! A
= (A +! A) * (B +! A)
= 1 * (B +! A)
= B +! A
[/ Editar]

existe &&! isDirectory || ! existe
= A * B +! A
= A * B +! A * 1 // Identidad
= A * B +! A * (B + 1) // Aniquilador
= A * B +! A * B +! A / / Distributividad e identidad
= B * (A +! A) +! A // Distributividad
= B * 1 +! A // Complementación 2
= B +! A // Identidad
=! IsDirectory || ! existe

O con doble complemento (!! x = x):

A * B +! A
= !! (A * B +! A)
=! (! (A * B) * A)
=! ((! A +! B) * A)
=! (! A * A + ! B * A)
=! (0 +! B * A)
=! (! B * A)
= B +! A
=! IsDirectory || ! existe

Eddie Gasparian
fuente
+1 por usar reglas formales (nunca pensé que vería una de estas después de mi primer año de universidad).
Nemanja Boric
5

No me gusta usar "!" cuando hay más de una condición en la expresión. Agregaré líneas de código para hacerlo más legible.

doesNotExist = !exists;
isFile = exists && !isDirecotry;
if (isFile || doesNotExist) 
   {}
David W
fuente
+1 Esto facilita la lectura como "si es un archivo o no existe", que está mucho más cerca del inglés.
Phil
Esta es una refactorización llamada Introducción a la variable explicativa .
Eddie Gasparian
1

Como se indicó anteriormente, la condición se puede reducir a:

if (!(exists && isDirectory))

Sin embargo, apuesto a que ser un directorio implica existencia. Si es así, podemos reducir la condición a:

if (!isDirectory)
Jack Applin
fuente