--------------------------------- Avoiding deep indentation in code --------------------------------- -Ian! D. Allen - idallen@idallen.ca My preference is to avoid deeply-nested code. It is hard to follow, doesn't print well on paper, and it's hard to read on the screen. This file deals with when you might violate structured programming principles to make your code less indented and easier to read and modify. http://safari.oreilly.com/0735619670/ch19lev1sec4 "Excessive indentation, or "nesting," has been pilloried in computing literature for 25 years and is still one of the chief culprits in confusing code. Studies by Noam Chomsky and Gerald Weinberg suggest that few people can understand more than three levels of nested ifs (Yourdon 1986a), and many researchers recommend avoiding nesting to more than three or four levels (Myers 1976, Marca 1981, and Ledgard and Tauer 1987a). Deep nesting works against what Chapter 5, "Design in Construction," describes as Software's Primary Technical Imperative: Managing Complexity. That is reason enough to avoid deep nesting" ----------------------------------------------------------------- Dropping unnecessary "else" clauses after "if" bodies that branch ----------------------------------------------------------------- To reduce indentation, I advocate removing "else" clauses on IF statements where either part of the IF branches elsewhere (meaning that the "else" isn't needed). Replace this code: if( something... ){ code1... exit(0); /* or return, break, continue, or goto ... */ } else { code2... } with this simpler code by removing the unnecessary "else": if( something... ){ code1... exit(0); /* or return, break, continue, or goto ... */ } code2... Replace this code: if( something... ){ code1... } else { code2... exit(0); /* or return, break, continue, or goto ... */ } with this simpler code by removing the unnecessary "else": if( ! something... ){ code2... exit(0); /* or return, break, continue, or goto ... */ } code1... Adding an ELSE clause after an IF clause that returns, breaks, continues, or exits is unnecessary; the ELSE is not needed since control will never flow around the ELSE. Don't do that. ------------------------------------------- Using "continue" instead of "else" in loops ------------------------------------------- To reduce excessive indentation, I advocate replacing this two-level code: while( something... ){ if( test1... ){ code1... } } with this one-level code that uses "continue" instead: while( something... ){ if( ! test1... ) continue; code1... } Any time an IF statement spans to the end of a loop, it can be replaced with a "continue" statement (reverse the IF test) and the rest of the code in the loop can move up one indentation level. A longer example: Replace this deeply nested code: while( something... ){ if( test1... ){ if( test2... ){ if( test3... ){ code... } } } } with this much less-indented code that uses "continue" instead: while( something... ){ if( ! test1... ) continue; if( ! test2... ) continue; if( ! test3... ) continue; code... } If the tests are simple and small, you can also write: while( something... ){ if( ! test1... || ! test2... || ! test3... ) continue; code... } The above can be less readable and harder to modify than having separate IF statements for each condition. Be careful. -------------------------------------- Deep nesting in structured programming -------------------------------------- Sometimes you want to select one of several alternatives in a loop. Here is some typical deeply-nested IF/ELSE code found in structured programming: while( something... ){ if( test1... ){ code1... } else { if( test2... ){ code2... } else { if( test3... ){ code3... } else { if( test4... ){ code4... } else { if( test5... ){ code5... } else { if( test6... ){ code6... } else { code7... } } } } } } } From the point of view of structured programming, the above 27 lines of code are good because they have one entry point (the top of the loop) and one exit point (the bottom of the loop). However, the code is hard to read and hard to modify. To add, delete, or re-order an IF clause requires changing the indentation of all the other nested IF clauses. Even though one alternative is pretty much of the same importance as any other, the lower alternatives each have to be indented to fit the IF/ELSE structure. The deeply indented code doesn't look like it's selecting one alternative from a list. Some relief from the deep indentation problems can be had by removing one redundant set of braces on each ELSE: while( something... ){ if( test1... ){ code1... } else if( test2... ){ code2... } else if( test3... ){ code3... } else if( test4... ){ code4... } else if( test5... ){ code5... } else if( test6... ){ code6... } else { code7... } } The above structured code is only 17 lines - a big improvement over the original 27-line version; but, the chained list of IF/ELIF tests still tangles together overall meaning of the code block, which is to do exactly *one* of the given alternatives in each loop iteration. Above, at least the re-ordering, adding, or deleting of IF clauses is easier than before; since, the indentation level is now more consistent. It's still a minor pain to swap the order of the first test (test1/code1) or the last test (code7) with any of the other alternatives, since they are coded differently. ----------------------------------------------------- Multiple continue points reduce need for deep nesting ----------------------------------------------------- Here is another version of the same code that produces the same output, without the deep nesting, and without the need to link the entire block of code together using ELSE/IF: while( something... ){ if( test1... ){ code1... continue; } if( test2... ){ code2... continue; } if( test3... ){ code3... continue; } if( test4... ){ code4... continue; } if( test5... ){ code5... continue; } if( test6... ){ code6... continue; } code7... } The above code is not good "structured programming", since the code has multiple loop continue points, not just one. However, the code is no longer than the original example, is much easier to modify, and is easy to read. You can add, modify, re-order, or delete IF clauses without changing any indentation. No "else" clauses are needed, since each IF clause simply "continue"s back to the top of the loop. (See the "Bad Code" example below.) As before, exactly one of the cases is executed in each loop iteration; but, each alternative is its own independent code block and the code is easier to read. The structure is similar to a switch() statement. Both the structured and unstructured versions of this code are correct; both get you full marks. My preference is for code that is easy to read, understand, and modify, even if the code violates "structured programming" rules. Deeply indented code is unreadable; fix it. Bad Code -------- This bad code looks like structured programming; but, it isn't: while( something... ){ if( test1... ){ code1... continue; } else { if( test2... ){ code2... continue; } else { if( test3... ){ code3... continue; } else { if( test4... ){ code4... continue; } else { if( test5... ){ code5... continue; } else { if( test6... ){ code6... continue; } else { code7... continue; } } } } } } } Since each IF statement ends with "continue", this isn't "pure" structured programming where the code inside the loop always gets to the bottom. Because of the "continue" statements, every following "ELSE" clause is superfluous - it just isn't needed. The above code has deep nesting without any of the benefists of structured programming. Marks off! ----------------- Nested Shell Code ----------------- See also (deep nesting shell programming example): http://teaching.idallen.com/cst8129/05f/notes/deep_nesting.txt "Less code is better code."