The issue of structure is that return/break/continue can allow you to break the concept of
structured code.
Your case doesn’t, but let’s use this example:
int foo() {
if(this_condition) {
if(that_condition)
return 0;
executable_code();
} else {
more_executable_code();
}
return 1;
}
As simple as this one is, you could rewrite it to be structured but things aren’t usually
that easy.
From: TUHS [mailto:tuhs-bounces@minnie.tuhs.org] On Behalf Of Don Hopkins
Sent: Thursday, November 9, 2017 10:41 AM
To: Noel Chiappa
Cc: tuhs(a)minnie.tuhs.org
Subject: Re: [TUHS] margins and indenting, and arbitrary c rules
On 9 Nov 2017, at 16:09, Noel Chiappa <jnc(a)mercury.lcs.mit.edu> wrote:
where xxx is a 'return', or a 'break', or a 'continue'. That
way, you can
empty your brain once you hit that, and start with a clean slate.
Absolutely — you nailed it! Not only do guard clauses reduce indentation, but they also
let you clear your mind and forget about as much as possible before running into the
complex stuff you will need all of your short term memory to understand, and which is then
more likely to fit on the screen.
https://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-ne…
Nested if statements have more “join” points where control flow merges back together at
the end instead of bailing out early, and the deeper and deeper nesting breaks up visual
patterns that would otherwise be apparent and wastes lots of space (on the page and in
your head).
“Guard clauses” with multiple returns can also make it easier to visually emphasize the
symmetries and patterns in the code.
I try to take every opportunity to break my code and expressions up across multiple lines
to emphasize repetition, patterns and variations, and I always use parens to explicitly
state which groupings I mean instead of depending on the reader to be a good enough
programmer who has memorized all the precedence rules to infer what I mean and hope I
didn’t slip up and make a mistake — I’m looking at YOU && and || !!!. (I’d fire
any programmer who mixed && and || without parens just to show off what a hot-shot
they were for remembering they have different precedence and trying to save a few bytes of
disk space at the expense of readability.)
For example, instead of:
float length = sqrt(x * x + y * y + z * z);
I go:
float length =
sqrt(
(x * x) +
(y * y) +
(z * z));
To me, that’s like getting a “triple word score” and a “double word score" in
scrabble, when I can arrange the code into pleasing patterns that explicitly shows the
repetition and progression of two factors by three dimensions, and arranges the x's,
y's and z's and *’s and +’s into three nice neat rows and columns reflecting the
structure of the expression.
The easier it is for your eyes to scan up and down the code to verify it’s right and
detect errors, the better, and the following example shows how guard clauses can do that:
See how much easier it is to spot the bug:
float length = sqrt(x * x + x * y + z * z);
float length =
sqrt(
(x * x) +
(x * y) +
(z * z));
http://wiki.c2.com/?GuardClause
Here is a sample of code using guard clauses ...
public Foo merge (Foo a, Foo b) {
if (a == null) return b;
if (b == null) return a;
// complicated merge code goes here.
}
Some style guides would have us write this with a single return as follows ...
public Foo merge (Foo a, Foo b) {
Foo result;
if (a != null) {
if (b != null) {
// complicated merge code goes here.
} else {
result = a;
}
} else {
result = b;
}
return result;
}
This second form has the advantage that the usual case, the merge, is dealt with first. It
also has a single exit as the last line of the routine which can be handy with some
refactorings. It has the disadvantage of separating the exceptional conditions from their
corresponding results which, in this case, makes it harder to see the symmetry of the
conditions. It also buries the usual, and complicated, case inside a couple of layers of
braces, which may make it harder to read.
The guards are similar to assertions in that both protect the subsequent code from special
cases. Guards differ from assertions in that they make a tangible contribution to the
logic of the method and thus cannot be safely omitted as part of an optimization. I
borrowed the term guard from <http://wiki.c2.com/?EwDijkstra> EwDijkstra when
naming this pattern. The first form above hints at the elegance of his guarded commands
though Dijkstra manages to save the single exit property in his code as well. --
<http://wiki.c2.com/?WardCunningham> WardCunningham