The transition from !signed_out? to signed_in? is entirely non-logical, so it's hard to see what it's doing in this post, which is supposedly illustrating logical laws (two of the most elementary logical laws, at that, that I'm surprised it would ever have occurred to someone to think needed introduction to an audience of professional programmers).
Yep, learned that back in college, more years ago than I should admit.<p>Experience then teaches that if the expression is complicated enough for that to matter, you've already lost. Instead, make a boolean function with explicit "short circuit" returns.<p><pre><code> // return true if we're screwed
isScrewed ( relevant parameters...):
if failure-mode-one:
return true
if failure-mode-two:
return true
if guaranteed-save-otherwise:
return false
if failure-mode-three:
return true
return false
</code></pre>
I remember seeing a horrible "if" statement that caused many thousands of dollars worth of wasted inventory back at one job cuz the clever coder thought he know the operator precedence and was saving time and money jamming a bunch of crap on one "if" line.<p>Now if I could just get coworkers to stop writing "fooFlag == true" and "fooFlag == false" :-)
Good naming conventions are pretty key here. My guess is the original writer of that code had used those in something else entirely, then reused those methods in a new method so he wouldn't have to rewrite.<p>I always feel like it's better to positively name Boolean values, personally, but I know everyone is different.
Something more useful I've found with DeMorgan is to flatten nested ifs:<p><pre><code> if (hasBread) {
// do something
}
</code></pre>
Can be flattened to:<p><pre><code> if (!hasBread) {
return
}
</code></pre>
As you start your function, flush out all the edge cases, invalid conditions and errors, then at every step forward in the function, you know you're always in a state were the odd balls have been taken care of and you deal with the general case.<p>This has two advantages:<p><pre><code> - it keeps the code tidy (the important parts are pretty
much never in a nested block).
- it makes you handle the odd balls explicitely.
</code></pre>
I really hate seeing functions such as :<p><pre><code> func cookBread() {
if (hasBread) {
do()
a()
bunch()
of()
stuff()
}
}</code></pre>
Old CS students' prank: improving the "no food and drink" sign unsurprisingly often found in labs by scribbling "no (food && drink) == (no food || no drink)" on it.
No! Don't stop there! Don't quit until you've refactored all of your conditionals to use NAND's! <a href="http://www.physicsforums.com/showthread.php?t=442775" rel="nofollow">http://www.physicsforums.com/showthread.php?t=442775</a>
Sorry, but I just don't see what was unclear about the original conditional. Anyone with a basic grasp of logic could parse it instantly.<p>As for the refactoring, that might be a good choice (I myself prefer positive boolean methods) but it's not a logic lesson.
So don't use non-assertive redundant uninverted comparisons or otherwise use positive conditionals unless the reverse is negative because it might not be clear or unless it is the opposite.
The distributivity law is also really helpful for simplifying conditionals. <a href="http://en.wikipedia.org/wiki/Boolean_algebra#Monotone_laws" rel="nofollow">http://en.wikipedia.org/wiki/Boolean_algebra#Monotone_laws</a>
While it's nice learn De Morgan's Law if you don't know it, it occurs to me that the problem of finding the simplest version of a given logical expression in n logical variables is a classic NP-complete problem (or NP-hard, the simpler question of whether the negation of an expression can be reduced to true is basically SAT).<p>There is no easy method to reduce every expression to a simple normal form - the conjunctive normal form of a given be exponentially larger than the original expression, etc.
This is why every developer should read Code Complete - it contains a myriad of tips learnt through many years of hard work.<p>Suffice to say that it contains such tips as avoiding negation in conditionals.<p>In the end it boils down to strategic optimization towards readability with the least mental overhead (the cycles you spend parsing, the more you can spend thinking).
I guess it depends on whether or not you want your code to read like natural language.<p>The refactored version reads like:<p>Allow access to the site if the user is signed in or has a trusted IP.<p>The original (DeMorgan's applied):<p>Allow access to the site if the user isn't signed out or doesn't have an untrusted IP.<p>It does help to have a good understanding of propositional logic and Boolean algebra, though.
I sometime get my IDE to rotate the various representations of a boolean equation to try to find a better looking one (it's not always the shortest, sometimes some concept make more sense, like in his example)
It's about Ruby conditionals, but they didn't talk about my favorite part.<p><pre><code> eat_gruel unless has_parents?
puts "Please sir, I want some more" if shortest_straw?</code></pre>