820 likes | 900 Views
Statements and Control Issues. Ch 17, 19 Michael Murphy CS 525 Software Engineering II Dr Xianzhong Liang. Sources. Code Complete; Steve McConnell www.answers.com C++ how To Program; Deitel & Deitel http://www.sparknotes.com/cs/recursion/whatisrecursion/section2.rhtml.
E N D
Statements and Control Issues Ch 17, 19 Michael Murphy CS 525 Software Engineering II Dr Xianzhong Liang
Sources • Code Complete; Steve McConnell • www.answers.com • C++ how To Program; Deitel & Deitel • http://www.sparknotes.com/cs/recursion/whatisrecursion/section2.rhtml
Chapter 17: Unusual Control Structures • Multiple Returns from a Routine • Recursion • goto
Multiple Returns • Multiple Returns from a Routine • Most languages support some means of exiting from a routine partway through the routine. • Return and exit statements are control constructs that enable a program to exit from a routine at will. • They cause the routine to terminate through the normal exit channel, returning control to the calling routine.
Multiple Returns • Use a return when it enhances readability • Readability or Necessity?
Multiple Returns Multiple Return from a Routine Comparison Compare( int value1, int value2 ) { if ( value1 < value2 ) { return Comparison_LessThan; } else if ( value1 > value2 ) { return Comparison_GreaterThan; } return Comparison_Equal; } (1)This routine returns a Comparison enumerated type.
Multiple Returns • Use guard clauses (early error checking returns or exits) to simplify complex error processing instead of nested if-then statements • In a routine that needs to check multiple conditions prior to executing the code, it can be more efficient and easier to read if each condition check exits the routing if it fails
Multiple Returns If file.validName() Then If file.Open() Then If encryptionKey.valid() Then If file.Decrypt( encryptionKey ) Then ' lots of code... End If End If End If End If
If Not file.validName() Then errorStatus = FileError_InvalidFileName Exit Sub End If If Not file.Open() Then errorStatus = FileError_CantOpenFile Exit Sub End If If Not encryptionKey.valid() Then errorStatus = FileError_InvalidEncryptionKey Exit Sub End If If Not file.Decrypt( encryptionKey ) Then errorStatus = FileError_CantDecryptFile Exit Sub End If ' lots of code...
Multiple Returns • Minimize the number of returns in each routine • It's harder to understand a routine when, reading it at the bottom, you're unaware of the possibility that it returned somewhere above. For that reason, use returns judiciously—only when they improve readability. • Multiple returns can enhance a routine's readability and maintainability, and they help prevent deeply nested logic.
Recursion • Recursion • In recursion, a routine solves a small part of a problem itself, divides the problem into smaller pieces, and then calls itself to solve each of the smaller pieces. • Recursion is usually called into play when a small part of the problem is easy to solve and a large part is easy to decompose into smaller pieces • Recursion provides elegant solutions to a small set of problems.
Recursion Types • Linear recursive function is a function that only makes a single call to itself each time the function runs • Tail recursion is a form of linear recursion. In tail recursion, the recursive call is the last thing the function does.
Linear Recursive Function int gcd(int m, int n) //greatest common denominator { int r; if (m < n) return gcd(n,m); r = m%n; if (r == 0) return(n); else return (gcd(n,r)); }
Binary Recursion • Binary Recursive is a function that calls itself two or more times. void QuickSort( int firstIndex, int lastIndex, String [] names ) { if ( lastIndex > firstIndex ) { int midPoint = Partition( firstIndex, lastIndex, names ); QuickSort( firstIndex, midPoint-1, names ); QuickSort( midPoint+1, lastIndex, names ); } }
Resursion Types • Exponential recursive function is one that, if you were to draw out a representation of all the function calls, would have an exponential number of calls in relation to the size of the data set. • Nested Recursion is a function where one of the arguments to the recursive function is the recursive function itself!
Recursion • Tips for Using (or not) Recursion • Make sure the recursion stops • Use safety counters to prevent infinite recursion. • Limit recursion to one routine • Cyclic recursion (A calls B calls C calls A) • Keep an eye on the stack • Don't use recursion for factorials or Fibonacci numbers
goto • A goto statement directs the computer to go to some other part of the program. Low-level language equivalents are "branch" and "jump.“ • In Fortran there were no presentable loop structures, so loops were programmed with goto’s.
goto • The Argument Against gotos • The general argument against goto’s is that code without goto’s is higher-quality code. • Code containing goto’s is hard to format • Use of goto’s defeats compiler optimizations. • Some optimizations depend on a program's flow of control residing within a few statements. An unconditional goto makes the flow harder to analyze and reduces the ability of the compiler to optimize the code. • Analyzing and verifying code is very difficult with goto statements.
goto • The Argument for gotos • Use rarely. • A well-placed goto can eliminate the need for duplicate code. • Duplicate code increases the size of source and executable files. • The bad effects of the goto are outweighed in such a case by the risks of duplicate code. Both have to be modified the same. • The goto is useful in a routine that allocates resources, performs operations on those resources, and then deallocates the resources.
goto • An example in using goto’s for error Processing
Sub PurgeFiles( ByRef errorState As Error_Code ) Dim fileIndex As Integer Dim fileToPurge As Data_File Dim fileList As File_List Dim numFilesToPurge As Integer MakePurgeFileList( fileList, numFilesToPurge ) errorState = FileStatus_Success fileIndex = 0 While ( fileIndex < numFilesToPurge ) fileIndex = fileIndex + 1 If Not ( FindFile( fileList( fileIndex ), fileToPurge ) ) Then errorState = FileStatus_FileFindError GoTo END_PROC End If If Not OpenFile( fileToPurge ) Then errorState = FileStatus_FileOpenError GoTo END_PROC End If If Not OverwriteFile( fileToPurge ) Then errorState = FileStatus_FileOverwriteError GoTo END_PROC End If if Not Erase( fileToPurge ) Then errorState = FileStatus_FileEraseError GoTo END_PROC End If Wend END_PROC: DeletePurgeFileList( fileList, numFilesToPurge ) End Sub
goto • Replace goto’s with nested if statements
While ( fileIndex < numFilesToPurge And errorState = FileStatus_Success ) fileIndex = fileIndex + 1 If FindFile( fileList( fileIndex ), fileToPurge ) Then If OpenFile( fileToPurge ) Then If OverwriteFile( fileToPurge ) Then If Not Erase( fileToPurge ) Then errorState = FileStatus_FileEraseError End If Else ' couldn't overwrite file errorState = FileStatus_FileOverwriteError End If Else ' couldn't open file errorState = FileStatus_FileOpenError End If Else ' couldn't find file errorState = FileStatus_FileFindError End If Wend DeletePurgeFileList( fileList, numFilesToPurge )
goto • Rewrite with a status variable
While ( fileIndex < numFilesToPurge ) And ( errorState = FileStatus_Success ) fileIndex = fileIndex + 1 If Not FindFile( fileList( fileIndex ), fileToPurge ) Then errorState = FileStatus_FileFindError End If If ( errorState = FileStatus_Success ) Then If Not OpenFile( fileToPurge ) The errorState = FileStatus_FileOpenError End IfEnd If If ( errorState = FileStatus_Success ) Then If Not OverwriteFile( fileToPurge ) Then errorState = FileStatus_FileOverwriteError End If End If If ( errorState = FileStatus_Success ) Then If Not Erase( fileToPurge ) Then errorState = FileStatus_FileEraseError End If End If Wend DeletePurgeFileList( fileList, numFilesToPurge )
goto • Rewrite with a try - finally
Sub PurgeFiles() Dim fileIndex As Integer Dim fileToPurge As Data_File Dim fileList As File_List Dim numFilesToPurge As Integer MakePurgeFileList( fileList, numFilesToPurge ) Try fileIndex = 0 While ( fileIndex < numFilesToPurge ) fileIndex = fileIndex + 1 FindFile( fileList( fileIndex ), fileToPurge ) OpenFile( fileToPurge ) OverwriteFile( fileToPurge ) Erase( fileToPurge ) Wend Finally DeletePurgeFileList( fileList, numFilesToPurge ) End Try End Sub
Comparison of Approaches • The goto approach avoids deep nesting and unnecessary tests but of course has gotos. • The nested-if approach avoids gotos but is deeply nested and gives an exaggerated picture of the logical complexity of the routine. • The status-variable approach avoids gotos and deep nesting but introduces extra tests. • The try-finally approach avoids both gotos and deep nesting but isn't available in all languages.
goto’s and Sharing Code in an else Clause • A case in which you have two conditional tests and an else clause and you want to execute code in one of the conditions and in the else clause. if ( statusOk ) { if ( dataAvailable ) importantVariable = x; goto MID_LOOP; } } else { importantVariable = GetValue(); MID_LOOP: // lots of code … }
Rewrite with the important code in its own routine. if ( statusOk ) { if ( dataAvailable ) { importantVariable = x; DoLotsOfCode( importantVariable ); } } else { importantVariable = GetValue(); DoLotsOfCode( importantVariable ); }
Example of Sharing Code in an else Clause Without a goto if ( ( statusOk && dataAvailable ) || !statusOk ) { if ( statusOk && dataAvailable ) { importantVariable = x; } else { importantVariable = GetValue(); } // lots of code ... }
goto Summary • Use gotos to emulate structured control constructs in languages that don't support them directly. • Don't use the goto when an equivalent built-in construct is available. • Measure the performance of any goto used to improve efficiency. Make sure to document reasons for using it. • Limit yourself to one goto label per routine unless you're emulating structured constructs. • Limit yourself to gotos that go forward, not backward, unless you're emulating structured constructs.
goto Summary • If goto statements are used, ensure all goto labels are used. • Make sure a goto doesn't create unreachable code. • If you're a manager, adopt the perspective that a battle over a single goto isn't worth the loss of the war. If the programmer is aware of the alternatives and is willing to argue, the goto is probably OK • Use goto’s as a last resort.
goto Summary • There will usually be a design option that will eliminate use of goto statements, use it!!!
Chapter 17 Conclusion • Multiple Returns from a Routine, use them judiciously, only when necessary. • Recursion, is great for a small set of problems. • Goto, don’t use it unless absolutely necessary.
Chapter 19: General Control Issues • Boolean Expressions • Compound Statements (Blocks) • Null Statements • Taming Dangerously Deep Nesting • A Programming Foundation: Structured Programming • Control Structures and Complexity
Boolean Expressions • Except for the simplest control structures, the structure that calls for the execution of statements in sequence, all control structures depend on the evaluation of boolean expressions.
Boolean Expressions • Use true and false instead of 0 and 1. • It makes the intent clear. • No explanation is needed • Compare values to true and false implicitly while ( not done ) ... while ( a > b ) ... rather than while ( done = false ) ... while ( (a > b) = true ) ...
Boolean Expressions • Break complicated tests into partial tests with new Boolean variables • Rather than creating a monstrous test with half a dozen terms, assign intermediate values to terms that allow you to perform a simpler test. • Move complicated expressions into Boolean functions • If a test is repeated often or distracts from the main flow of the program, move the code for the test into a function and test the value of the function.
Boolean Expressions • Use decision tables to replace complicated conditions • If there is a complicated test involving several variables. It can be helpful to use a decision table to perform the test rather than using ifs or cases. • A decision-table lookup is easier to code initially, having only a couple of lines of code and no control structures. • However, if your data changes, you may have to change the table, but not the code.
Boolean Expressions • Forming Boolean Expressions Positively • Double negatives make a positive, but are confusing !statusNotOK = OK or not OK? !notDone = Done or Not done? • These are !OK!!!
Boolean Expressions • Apply DeMorgan's Theorems to simplify boolean tests with negatives if ( !displayOK || !printerOK ) ... This is logically equivalent to the following: if ( !( displayOK && printerOK ) ) ...
Boolean Expressions • Transformations of Logical Expressions Under DeMorgan's Theorems Initial ExpressionEquivalent Expression not A and not B not ( A or B ) not A and B not ( A or not B ) A and not B not ( not A or B ) A and B not ( not A or not B ) not A or not B not ( A and B ) not A or B not ( A and not B ) A or not B not ( not A and B ) A or B not ( not A and not B )
Boolean Expressions • Using Parentheses to Clarify Boolean Expressions • Instead of this: • if ( a < b == c == d ) ... • Use this: • if ( ( a < b ) == ( c == d ) ) ...
Boolean Expressions • Fully parenthesize logical expressions • Parentheses are cheap, and they aid readability. Fully parenthesizing logical expressions as a matter of habit is good practice. • Use a simple counting technique to balance parentheses if ( ( ( a < b ) == ( c == d ) ) && !done ) ... 0 1 2 3 2 3 2 1 0
Boolean Expressions • Know How Boolean Expressions Are Evaluated • Many languages have an implied form of control during the evaluation of boolean expressions. Compilers for some languages evaluate each term in a boolean expression before combining the terms and evaluating the whole expression. while ( i < MAX_ELEMENTS and item[ i ] <> 0 ) ... • If this whole expression is evaluated, you'll get an error on the last pass through the loop. The variable i equals maxElements, so the expression item[ i ] is equivalent to item[ maxElements ], which is an array-index error. • The code could be restructured so that the error doesn't occur: while ( i < MAX_ELEMENTS ) if ( item[ i ] <> 0 ) then ...
Common Problems with Booleans • In C-derived languages, put constants on the left side of comparisons • Do you forget == and use =? • Use: if ( MIN_ELEMENTS = i ) ... • The compiler should flag the single = as an error since assigning anything to a constant is invalid. • In contrast, the compiler will flag this only as a warning, and only if you have compiler warnings fully turned on: if ( i = MIN_ELEMENTS ) ... • So you just set i to the value of MIN_ELEMNENTS!!
Common Problems with Booleans • In C++, consider creating preprocessor macro substitutions for &&, ||, and == (but only as a last resort) • Using = when you mean == is an easy mistake to make. If you get stung often by this one, you might create a macro like EQUALS for logical equals (==). • Why do this?
Common Problems with Booleans • In Java, know the difference between a==b and a.equals(b) • In Java, a==b tests for whether a and b refer to the same object, whereas a.equals(b) tests for whether the objects have the same logical value. In general, Java programs should use expressions like a.equals(b) rather than a==b.
Use of Zero • Guidelines for Comparisons to 0 • Zero can be used for many things. • It's a numeric value. • It's a null terminator in a string. • It's the value of a null pointer. • It's the value of the first item in an enumeration. • It's false in logical expressions. • Because it's used for so many purposes, you should write code that highlights the specific way 0 is used.