Tom Duff: Reading Code From Top to Bottom (1999)
iq0.com
iq0.com
Again, switch, duff's device & other computed-goto hacks are "constructs", a chunk of "decision" code that sits in the middle of the page directing traffic. Pattern-matching is far more sublime than that: along with manifesting itself as a visual construct, it can also hide in the head of the function, cozzying up with its parameters, and also in binding/assignment forms.
A distant, but weaker cousin of pattern-matching is destructuring, more common in Lisps.
EDIT: It makes a lot more sense to me now that I think about it in the context of a language like C, with no garbage collection and no exceptions. Thanks!
I never use goto, but I often use multiple returns similar to the examples in this article.
I have sadly code reviewed many pieces of code with the nesting problem.
I recall that Linus has advocated the use of goto in the Linux kernel for similar reasons. And the goto version of this code does seem more readable to me: http://kerneltrap.org/node/553/2131
You should be handling every case anyway - by the time the code is done, there are no exceptional situations left ;)
Also makes it easier to step through code ("I'll just set a breakpoint and see what this function returns...").
I usually advocate using early returns for near boilerplate things like parameter checking, then a single return that does cleanup and exit, with 'goto' encouraged as the proxy for a return-in-the-middle. I think of it as similar to what a C++ compiler does for you.
These days I code for maintenance, since someone /is/ going to go in and start adding early returns and subsequent bugs. Plan for code rot.
http://iq0.com/notes/trig.html All you need to know about Trignometry
http://iq0.com/notes/frobenius.html The Frobenius-Burnside Counting Formula
http://iq0.com/notes/geometric.html Sum of a Geometric Series
cont = 1;
while (cont)
{
foo = bar();
switch (foo)
{
case 0:
cont = 0;
break;
default:
// other work
break;
}
}
You can do this instead: for (;;)
{
foo = bar();
switch (foo)
{
case 0:
break;
default:
// other work
continue;
}
break;
}
IMO the idiom, once you're used to it, reads better.(Yes, I know you can reduce the lines further e.g. putting foo=bar() into the for statement, but it's the multi-level break I'm focused on here.)
while( (foo = bar()) != 0 )
{
/* other work */
}
Or even simpler (since 'foo' serves no real purpose in the example): while( bar() )
{
/* other work */
}int some_function( some_variable ) {
if (some_variable) {
b = 1;
} else {
b = 2;
}
log("Its important I log that I'm returning %d\n", b);
return b;
}If its not obvious why this is superior...
int some_function( some_variable ) {
log("Its important I log that I'm returning %d\n", (some_variable ? 1 : 2));
return (some_variable ? 1 : 2);
} some_function = function() {
if (...) return 1;
if (...) return 2;
}.log("It's important I log that I'm returning"); func someFunction(b bool) (i int) {
defer func() {
log.Printf("I'm returning %d", i)
}()
if b {
return 1
}
return 0
}
For more on defer, see this blog post: http://blog.golang.org/2010/08/defer-panic-and-recover.html