Como posso reformatar minha condição para torná-la melhor?


35

Eu tenho uma condição

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

como posso modificá-lo, para que seja mais compreensível.


11
que valor tem o isDirectory quando existe é falso?
MarkThan # 5/12

11
existe é tipo Bool, isDirectory é também variáveis tipo BOOL
Spynet

5
se (! isDirectory) ... (existe ||! existe) sempre será verdadeiro.
Tubarão

@ Shark - E se existse isDirectorysão verdadeiras?
pasawaya

Eu li o título como "Eu tenho um problema de personalidade, posso apagar minha mente para corrigi-lo". Sim, estou cansada.
Annan

Respostas:


110

|| é comutativo, então

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

é equivalente.

Agora, porque existe sempre é verdade na segunda parte do, ||você pode soltar o &&:

if(!exists || !isDirectory)

Ou você pode dar um passo adiante e fazer:

if(!(exists && isDirectory))

5
O que foi implícito, mas não mencionado explicitamente aqui, é que &&tem maior precedência (pelo menos na maioria dos idiomas conhecidos - pode haver exceções) ||. Assim, a && b || cé equivalente a, (a && b) || cmas não a a && (b || c).
Péter Török

27
Eu acho que !exists || !isDirectoryé mais "compreensível", porque, isDirectorynão pode ser verdade se !exists. Então, como humano, diremos "se ele não existe ou se existe e não é um diretório".
duros

6
Eu prefiro! Existe || ! isDirectory sobre o último.
Apoorv Khurasia

3
||é comutativo apenas se usado em valores sem efeitos colaterais - se, por exemplo, usado com funções, algumas funções podem não ser chamadas (curto-circuito) ou retornar um valor diferente em uma ordem diferente.
Orlp 05/10/12

26
Qualquer um que confie na precedência relativa de '&&', '||', '==', '! =', Etc, e não deixe clara sua intenção usando colchetes, merece ser atingido. Em todos os idiomas, algo como 'a && b || c 'é equivalente a um comentário dizendo que o autor provavelmente estragou tudo na pressa para evitar digitar alguns caracteres extras.
Brendan

51

Como processo, sugiro construir uma tabela de verdade:

e = exists
d = isDirectory

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

Isso corresponde à NANDoperação , que é simplesmente:

!(exists && isDirectory)

Se você não se lembra de todos os seus portais lógicos, a wikipedia tem uma boa referência com as tabelas da verdade para inicializar .


@Christoffer Hammarström levantou um ponto importante sobre o estado de isDirectoryamarração ao estado de exists. Supondo que eles se refiram à mesma referência e que não é possível ter um estado em que a referência não exista e seja um diretório, a tabela verdade pode ser escrita da seguinte maneira:

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

O n/aé usado para representar um estado que não importa. Reduções aceitáveis ​​podem resultar em um 1ou 0para os estados que resultam em n/a.

Com isso em mente, !(exists && isDirectory)ainda é uma redução válida, resultando em um 1para !e && d.

No entanto, !isDirectoryseria uma redução muito mais simples, resultando em 0para !e && d.


4
O próximo passo é perceber que isso isDirectorydepende exists. Não pode ser um diretório e não existir.
Christoffer Hammarström

@ChristofferHammarstrom, Fora de contexto, não posso assumir que as variáveis ​​se refiram à mesma coisa, mas esse é um ponto válido. A coluna de resultados deve ser preenchida n/aem locais onde o estado é impossível de alcançar e a equação reduzida em conformidade.
ZzzzBov 06/10/12

Bem, se as variáveis ​​se referem a dois contextos diferentes, elas são muito concisas e precisam ser renomeadas.
Christoffer Hammarström

Mas construir uma tabela verdade e avaliá-la é NP-completo!
Thomas Eding

@ThomasEding, tenho duas citações para você: "Na teoria, teoria e prática são iguais; na prática, elas não são". e "Otimização prematura é a raiz de todo mal".
zzzzBov

22

Para melhor legibilidade, eu gosto de extrair condições booleanas para métodos:

if(fileNameUnused())
{...}

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

Ou com um nome de método melhor. Se você pode nomear esse método corretamente, o leitor do seu código não precisa descobrir o que significa a condição booleana.


+1 por dizer algo sobre nomes úteis. Mas em algum lugar você terá que reformatar a condicional.
Apoorv Khurasia

4
Uma alternativa menos extrema, que ainda transmite intenção, é apenas para nomear a condição usada:boolean fileNameUnused = !exists || !isDirectory; if (fileNameUnused) { doSomething(); }
Steven

8

Você pode apenas tentar acertar o caso de não-ir e sair se isso aparecer.

while(someCondition) {

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

        // the rest of the code
}

ou mesmo

function processFile(someFile)
{ 
    // ...
    if(exists && isDirectory)
       return false;
    // the rest of the code
    // ...
}

A declaração de interrupção, continuação e mais de uma declaração de retorno é considerada um cheiro de código?
Freiheit

8
@ Freiheit Depende do contexto. Às vezes, uma declaração de retorno antecipado é usada para reduzir o recuo, melhorando a legibilidade.
marco-Fiset

Melhor resposta - condicionais complexos perdem enormes quantidades de tempo lendo e entendendo-os com precisão. Como resultado, muitas vezes são "tomados como lidos", levando a erros insidiosos.
mattnz

6

Você pode usar uma tabela verdade, conforme indicado. O segundo passo pode ser um mapa KV para minimizar o número de termos.

Usar as leis da álgebra booleana é outra abordagem:

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

&& = *
|| = +

[Editar]
Uma transformação mais simples, porque as operações AND e OR são mutuamente distributivas:

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

existe o &&! isDirectory || ! existe
= A * B +! A
= A * B +! A * 1 // Identidade
= A * B +! A * (B + 1) // Annihilator
= A * B +! A * B +! A / / Distribuutividade e identidade
= B * (A +! A) +! A // Distributividade
= B * 1 +! A // Complementação 2
= B +! A // Identidade
=! IsDirectory || !existe

Ou com complemento duplo (!! 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


+1 por usar regras formais (nunca pensei em ver uma delas depois do meu primeiro ano de faculdade).
Nemanja Boric


5

Eu não gosto de usar "!" quando houver mais de uma condição na expressão. Vou adicionar linhas de código para torná-lo mais legível.

doesNotExist = !exists;
isFile = exists && !isDirecotry;
if (isFile || doesNotExist) 
   {}

+1 Isso facilita a leitura como "se é um arquivo ou não existe", muito mais próximo do inglês.
Phil

Essa é uma refatoração chamada Introduzir explicação explicativa .
Eddie Gasparian

1

Conforme indicado anteriormente, a condição pode ser reduzida para:

if (!(exists && isDirectory))

No entanto, aposto que ser um diretório implica existência. Nesse caso, podemos reduzir a condição para:

if (!isDirectory)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.