Comment puis-je reformater ma condition pour l'améliorer?

35

J'ai une condition

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

Comment puis-je le modifier pour qu'il soit plus compréhensible?

Spynet
la source
1
Quelle est la valeur de isDirectory quand existe est faux?
marktani
1
exist est de type Bool, isDirectory est aussi des variables de type BOOL
Spynet
5
if (! isDirectory) ... (existe ||! existe) sera toujours vrai.
Requin
@Shark - Et si existset isDirectorysont les deux vrais?
Pasawaya
J'ai lu le titre comme "J'ai un problème de personnalité, puis-je effacer mon esprit pour le réparer". Oui, je suis fatigué.
Annan

Réponses:

110

|| est si commutatif

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

est équivalent.

Maintenant, car existe est toujours vrai dans la deuxième partie de la, ||vous pouvez laisser tomber le &&:

if(!exists || !isDirectory)

Ou vous pouvez aller plus loin et faire:

if(!(exists && isDirectory))
monstre à cliquet
la source
5
Ce qui était implicite mais non explicitement mentionné ici est que la &&priorité est plus grande (du moins dans les langues les plus connues - il peut y avoir des exceptions) ||. Ainsi a && b || cest équivalent à (a && b) || cmais pas à a && (b || c).
Péter Török
27
Je pense que !exists || !isDirectoryc'est plus "compréhensible", parce que, isDirectoryne peut être vrai si !exists. Donc, en tant qu'être humain, nous dirons "s'il n'existe pas ou s'il [existe et que ce n'est pas un répertoire".
dur
6
Je préfère! Existe || ! isDirectory sur le dernier.
Apoorv Khurasia
3
||est uniquement commutatif si elle est utilisée sur des valeurs sans effets secondaires - si, par exemple, elle est utilisée avec des fonctions, certaines fonctions peuvent ne pas être appelées (court-circuit) ou renvoyer une valeur différente dans un ordre différent.
Orlp
26
Quiconque se fie à la priorité relative de '&&', '||', '==', '! =', Etc. et ne précise pas son intention en utilisant des parenthèses mérite d'être tiré. Dans toutes les langues, quelque chose comme 'a && b || c 'est équivalent à un commentaire disant que l'auteur avait probablement tout gâché pour ne pas avoir à saisir quelques caractères supplémentaires.
Brendan
51

En tant que processus, je suggère de construire une table de vérité:

e = exists
d = isDirectory

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

Cela correspond à l' NANDopération , qui est simplement:

!(exists && isDirectory)

Si vous ne vous souvenez pas de toutes vos portes logiques, Wikipedia a une bonne référence avec les tables de vérité à démarrer .


@Christoffer Hammarström a soulevé un point important à propos de l’état d’ isDirectoryêtre lié à l’état de exists. En supposant qu'ils se réfèrent à la même référence et qu'il n'est pas possible d'avoir un état où la référence n'existe pas et constitue un répertoire, la table de vérité peut être écrite comme suit:

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

Le n/aest utilisé pour représenter un état qui n'a pas d'importance. Réductions acceptables pourraient entraîner soit 1ou 0les états résultant en n/a.

Dans cet esprit, !(exists && isDirectory)est toujours une réduction valide, entraînant un 1pour !e && d.

Cependant, !isDirectoryserait une réduction beaucoup plus simple, ce qui 0pour !e && d.

zzzzBov
la source
4
La prochaine étape consiste à réaliser que cela isDirectorydépend exists. Il ne peut pas être à la fois un répertoire et ne pas exister.
Christoffer Hammarström
@ChristofferHammarstrom, Hors contexte, je ne peux pas supposer que les variables font référence à la même chose, mais c'est un argument valable. La colonne de résultat doit être remplie aux n/aendroits où l'état est impossible à atteindre et l'équation doit être réduite en conséquence.
zzzzBov
Eh bien, si les variables font référence à deux contextes différents, elles sont trop concises et doivent être renommées.
Christoffer Hammarström
Mais construire une table de vérité et l’évaluer est NP-complet!
Thomas Eding
@ThomasEding, j'ai deux citations pour vous alors: "En théorie, théorie et pratique sont identiques; en pratique, elles ne le sont pas." et "l'optimisation prématurée est la racine de tout le mal."
zzzzBov
22

Pour une meilleure lisibilité, j'aime bien extraire des conditions booléennes aux méthodes:

if(fileNameUnused())
{...}

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

Ou avec un meilleur nom de méthode. Si vous pouvez nommer cette méthode correctement, le lecteur de votre code n’a pas besoin de comprendre ce que signifie la condition booléenne.

Puckl
la source
+1 pour avoir dit quelque chose sur les noms utiles. Mais quelque part, vous devrez reformater le conditionnel.
Apoorv Khurasia
4
Une alternative moins extrême, qui traduit toujours une intention, consiste simplement à nommer la condition utilisée:boolean fileNameUnused = !exists || !isDirectory; if (fileNameUnused) { doSomething(); }
Steven,
8

Vous pouvez simplement essayer de résoudre le problème du non-départ et de renflouer si cela se présente.

while(someCondition) {

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

        // the rest of the code
}

ou même

function processFile(someFile)
{ 
    // ...
    if(exists && isDirectory)
       return false;
    // the rest of the code
    // ...
}
ZJR
la source
Break, ne continue-t-il pas, et plus d'une déclaration de retour considérée comme du code sent?
Freiheit
8
@Freiheit Cela dépend du contexte. Parfois, une déclaration de retour rapide est utilisée pour réduire le retrait, améliorant ainsi la lisibilité.
marco-fiset
Meilleure réponse: les conditions complexes perdent énormément de temps à lire et à les comprendre correctement. En conséquence, ils sont souvent "pris en lecture", ce qui conduit à des bugs insidieux.
Mattnz
6

Vous pouvez utiliser une table de vérité comme indiqué. La deuxième étape pourrait être une carte KV pour minimiser le nombre de termes.

Utiliser les lois de l’algèbre booléenne est une autre approche:

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

&& = *
|| = +

[Edit]
Une transformation plus simple, car les opérations AND et OR sont mutuellement distributives:

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

existe &&! isDirectory || ! existe
= A * B +! A
= A * B +! A * 1 // Identité
= A * B +! A * (B + 1) // Annihilateur
= A * B +! A * B +! A / / Distributivité et identité
= B * (A +! A) +! A // Distributivité
= B * 1 +! A // Complémentation 2
= B +! A // Identité
=! IsDirectory || ! existe

Ou avec double complément (!! 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
la source
+1 pour l'utilisation de règles formelles (je n'ai jamais pensé en voir une après ma première année d'université).
Nemanja Boric
5

Je n'aime pas utiliser "!" quand il y a plus d'une condition dans l'expression. Je vais ajouter des lignes de code pour le rendre plus lisible.

doesNotExist = !exists;
isFile = exists && !isDirecotry;
if (isFile || doesNotExist) 
   {}
David W
la source
+1 Cela rend plus facile la lecture comme "if is file or not n'existe pas" ce qui est beaucoup plus proche de l'anglais.
Phil
Il s’agit d’un refactoring appelé Introduire une variable explicative .
Eddie Gasparian
1

Comme indiqué précédemment, la condition peut être réduite à:

if (!(exists && isDirectory))

Cependant, je parie qu'être un répertoire implique l'existence. Si tel est le cas, nous pouvons réduire la situation à:

if (!isDirectory)
Jack Applin
la source