Как я могу переформатировать свое состояние, чтобы сделать его лучше?

35

У меня есть состояние

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

как я могу изменить это, чтобы это было более понятно.

Spynet
источник
1
какое значение имеет isDirectory, когда существует ложь?
Марктани
1
существует тип Bool, isDirectory также является переменным типа BOOL
Spynet
5
if (! isDirectory) ... (существует ||! существует) всегда будет истинным.
Акула
@Shark - Что, если existsи isDirectoryоба правда?
pasawaya
Я читаю заголовок как «У меня проблема с личностью, могу ли я стереть свой разум, чтобы ее исправить». Да, я устал
Аннан

Ответы:

110

|| коммутативно так

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

эквивалентно.

Теперь, поскольку существует всегда верно во второй части, ||вы можете отбросить &&:

if(!exists || !isDirectory)

Или вы можете пойти дальше и сделать:

if(!(exists && isDirectory))
чокнутый урод
источник
5
То, что подразумевалось, но явно не упомянуто здесь, &&имеет более высокий приоритет (по крайней мере, в большинстве известных языков - могут быть исключения), чем ||. Таким образом a && b || c, эквивалентно, (a && b) || cно не к a && (b || c).
Петер Тёрёк
27
Я думаю, что !exists || !isDirectoryэто более "понятно", потому что isDirectoryне может быть правдой, если !exists. Итак, как человек, мы скажем: «если он не существует или он [существует, и он] не является каталогом».
Duros
6
Я предпочитаю! Существует || ! isDirectory поверх последнего.
Апурв Хурасия
3
||является коммутативным, только если используется со значениями без побочных эффектов - если, например, он используется с функциями, некоторые функции могут не вызываться (короткое замыкание) или возвращать другое значение в другом порядке.
orlp
26
Любой, кто полагается на относительный приоритет '&&', '||', '==', '! =' И т. Д. И не раскрывает свое намерение с помощью скобок, заслуживает того, чтобы его застрелили. На всех языках что-то вроде 'a && b || c 'эквивалентен комментарию, в котором говорится, что автор, вероятно, все испортил, чтобы избежать ввода нескольких дополнительных символов.
Брендан
51

В качестве процесса я предлагаю создать таблицу истинности:

e = exists
d = isDirectory

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

Это соответствует NANDоперации , которая просто:

!(exists && isDirectory)

Если вы не помните все свои логические элементы, в Википедии есть хорошая ссылка с таблицами истинности для загрузки .


@Christoffer Hammarström поднял важный вопрос о состоянии isDirectory привязанности к состоянию exists. Предполагая, что они ссылаются на одну и ту же ссылку, и что невозможно иметь состояние, когда ссылка не существует и является каталогом, таблицу истинности можно записать следующим образом:

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

n/aИспользуется для обозначения состояния , которое не имеет значения. Приемлемые сокращения могут привести к тому 1или 0иному состоянию, что приведет к n/a.

Имея это в виду, !(exists && isDirectory)все еще действительное сокращение, в результате чего 1для !e && d.

Тем не менее, !isDirectoryбудет гораздо проще сократить, в результате чего 0для !e && d.

zzzzBov
источник
4
Следующий шаг - осознать, что isDirectoryзависит от exists. Он не может быть как каталогом, так и не существовать.
Кристофер Хаммарстрем
@ChristofferHammarstrom, вне контекста не могу предположить, что переменные ссылаются на одно и то же, но это верная точка. Столбец результатов должен быть заполнен n/aв тех местах, где невозможно достичь состояния, и уравнение должно быть соответственно уменьшено.
zzzzBov
Хорошо, если переменные ссылаются на два разных контекста, то они слишком кратки и должны быть переименованы.
Кристофер Хаммарстрем
Но построение таблицы истинности и ее оценка является NP-полной!
Томас Эдинг
@ThomasEding, у меня есть две цитаты для вас: «Теоретически, теория и практика одинаковы; на практике это не так». и «Преждевременная оптимизация - корень всего зла».
zzzzBov
22

Для лучшей читаемости я хотел бы извлечь логические условия для методов:

if(fileNameUnused())
{...}

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

Или с лучшим именем метода. Если вы можете правильно назвать этот метод, читателю вашего кода не нужно выяснять, что означает логическое условие.

Puckl
источник
+1 за то, что сказал что-то о полезных именах. Но где-то вам придется переформатировать условное.
Апурв Хурасия
4
Менее крайняя альтернатива, которая все еще выражает намерение, это просто назвать используемое условие:boolean fileNameUnused = !exists || !isDirectory; if (fileNameUnused) { doSomething(); }
Стивен
8

Вы могли бы просто попытаться прибить случай без дела и выручить, если это обнаружится.

while(someCondition) {

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

        // the rest of the code
}

или даже

function processFile(someFile)
{ 
    // ...
    if(exists && isDirectory)
       return false;
    // the rest of the code
    // ...
}
Zjr
источник
Не является ли оператор break, continue и более одного возврата возвращаемым запахом кода?
Freiheit
8
@Freiheit Это зависит от контекста. Иногда оператор раннего возврата используется для уменьшения отступов, что повышает удобочитаемость.
Марко-Фисет
Лучший ответ - сложные условные выражения тратят огромное количество времени на чтение и точное их понимание. В результате часто «воспринимаются как прочитанные», что приводит к коварным ошибкам.
Mattnz
6

Вы можете использовать таблицу истинности, как указано. Вторым шагом может быть KV-карта для минимизации количества терминов.

Использование законов булевой алгебры - это еще один подход:

A = существует
B =! IsDirectory
! A =! Существует

&& = *
|| = +

[Править]
Более простое преобразование, потому что операции И и ИЛИ являются взаимно-распределительными:

существует &&! isDirectory || ! Существует
= A * B +! A
= (A +! A) * (B +! A)
= 1 * (B +! A)
= B +! A
[/ Edit]

существует &&! isDirectory || ! Существует
= A * B +! A
= A * B +! A * 1 // Идентичность
= A * B +! A * (B + 1) // Аннулятор
= A * B +! A * B +! A / / Распределение и идентификация
= B * (A +! A) +! A // Распределение
= B * 1 +! A // Дополнение 2
= B +! A // Идентичность
=! IsDirectory || !существует

Или с двойным дополнением (!! 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 || !существует

Эдди Гаспарян
источник
+1 за использование формальных правил (я никогда не думал, что увижу одно из них после первого года обучения в колледже).
Неманья Борич
wolframalpha.com/input/?i=A+%26%26+B+||+!A
Эдди Гаспарян
5

Я не люблю использовать "!" когда в выражении более одного условия. Я добавлю строки кода, чтобы сделать его более читабельным.

doesNotExist = !exists;
isFile = exists && !isDirecotry;
if (isFile || doesNotExist) 
   {}
Дэвид В.
источник
+1 Это облегчает чтение как «если файл или не существует», что намного ближе к английскому.
Фил
Это рефакторинг под названием « Объяснить переменную» .
Эдди Гаспарян
1

Как указывалось ранее, условие может быть уменьшено до:

if (!(exists && isDirectory))

Тем не менее, держу пари, что наличие каталога подразумевает существование. Если это так, мы можем уменьшить условие до:

if (!isDirectory)
Джек Апплин
источник