6.001 Structure and Interpretation of Computer Programs. Copyright o 2004 by Massachusetts Institute of Technology Slide 7.1.16 In general, taking care to meet each of the stages when you Documenting code create code will often ensure an easier time when you have to efine or replace code. Getting into the habit of doing this every time you write something, even if you are only minutes Expected or required types of arguments Type of returned value away from some problem set deadline, will greatly improve List of constraints that must be satisfied by arguments or stages of computation Expected state of computation at key points in <: 8n2003 6 001 SICP 6.001 Notes: Section 7.2 Slide 7.2.1 While we would like to believe that the code we write will al ways run correctly, the first time we try it, experience shows Debugging errors that this is a fortunate happenstance. Typically, especially with. Common sources of errors complexcodethingswillnotworkrightandweneedtodebug.commontoolstodebug our code. Debugging is in part an acquired skill -with lots of practice you will develop your own preferred approach. Here we are going to describe some of the common sources of errors in code, and standard tools for finding the causes of the errors and fixing them Slide 7.2.2 Common errors A common and simple bug in code arises when we use an unbound variable. From the perspective of Scheme, this means that somewhere in our code we try to reference(or look up the value of)a variable that does not have one. This can Solution: search for instance occur for several reasons. The simplest is that we mistyped-a spelling error. The solution in this pretty straightforward-simply search through the code file using editor tools to find the offending instance and correct it 6 001 SICP
6.001 Structure and Interpretation of Computer Programs. Copyright © 2004 by Massachusetts Institute of Technology. Slide 7.1.16 In general, taking care to meet each of the stages when you create code will often ensure an easier time when you have to refine or replace code. Getting into the habit of doing this every time you write something, even if you are only minutes away from some problem set deadline, will greatly improve your productivity! 6.001 Notes: Section 7.2 Slide 7.2.1 While we would like to believe that the code we write will always run correctly, the first time we try it, experience shows that this is a fortunate happenstance. Typically, especially with complex code, things will not work right, and we need to debug our code. Debugging is in part an acquired skill – with lots of practice you will develop your own preferred approach. Here, we are going to describe some of the common sources of errors in code, and standard tools for finding the causes of the errors and fixing them. Slide 7.2.2 A common and simple bug in code arises when we use an unbound variable. From the perspective of Scheme, this means that somewhere in our code we try to reference (or look up the value of) a variable that does not have one. This can occur for several reasons. The simplest is that we mistyped – a spelling error. The solution in this case is pretty straightforward – simply search through the code file using editor tools to find the offending instance and correct it
6.001 Structure and Interpretation of Computer Programs. Copyright o 2004 by Massachusetts Institute of Technology Slide 7.2.3 Sometimes, however, we are using a legal variable(that is, one that we intended to hold some value) but the evaluator still Common errors complains that this variable is unbound. How can that be? Remember that in Scheme a variable gets bound to a value in Unbound variable may directly tell the interpreter to give a variable some valie e one of several ways. We may define it at top level", that is, we Cause: typo olution search for instance We may define it internally within some procedure. Or, we Unbound variable may use it as a formal parameter of a procedure, in which case Solution it gets locally bound to a value when the procedure is applied In the last two cases, if we attempt to reference the variable Use debugging tools to isolate instance outside the scope of the binding, that is, somewhere outside the 4 6001 sICP bounds of the lambda expression in which the variable is being used. we will get an unbound variable error. This means that we have tried to use a variable outside its le domain, and we need to correct this. This probably means we have a coding error, but we can isolate the problem either by searching for instances of the variable in the code file, or by using the debugger Slide 7.2.4 The Debugger So what does a debugger do to help us find errors? Each programming language will have its own flavor of debugger Places user inside state of computation at time of for an interpreted language like Scheme, the debugger actually places us inside the state of the computation. That is, when an error occurs, the debugger provides us access to the state of the Reductions computation at the time of the error, including access to the Can examine bindings of variables and parameters aues of the variables within the computation. Moreover, we can step around inside the environment of the computation: we can work back up the chain of computational steps, examining I what values were produced during reductions(where computation is reduced to a simpler expression), and examin what values were produced during substitutions(where the computation was converted to a simpler version or ng itself Side.2.5 Debugger example For example, here is a simple procedure, which we have called nea.dentify atack fran with argument 2. Notice what happens when we hit the be⊥0M unbound variable error and enter the debugger. We are placed The "TRS at the spot in the computation at which the error occurred. If /=so baena varaszst bes 4f(=n0 see what expressions were reduced to get to this point, and what =1 ne c a w/ aE t te c-a 1) we choose to step back through the chain of evaluations, we can mo baf a a oI bar (e a to ( all 4+n(fon(-n1))) recursive versions of the same problem were invoked in reaching this stage In this case. we note that foo initially called with Foo (-n 1 rgument 2, and after a reduction through an if expression, 4 mm we arrived at an expression that contained within it a simpler coming from within the body of foo and is in the base case of the decision process a version of the same problem. This reduction stage repeated again, until we apparently reached the base case of the if expression, where we hit the unbound variable. We can see in this simple case that our unbound error is
6.001 Structure and Interpretation of Computer Programs. Copyright © 2004 by Massachusetts Institute of Technology. Slide 7.2.3 Sometimes, however, we are using a legal variable (that is, one that we intended to hold some value) but the evaluator still complains that this variable is unbound. How can that be? Remember that in Scheme a variable gets bound to a value in one of several ways. We may define it at “top level”, that is, we may directly tell the interpreter to give a variable some value. We may define it internally within some procedure. Or, we may use it as a formal parameter of a procedure, in which case it gets locally bound to a value when the procedure is applied. In the last two cases, if we attempt to reference the variable outside the scope of the binding, that is, somewhere outside the bounds of the lambda expression in which the variable is being used, we will get an unbound variable error. This means that we have tried to use a variable outside its legal domain, and we need to correct this. This probably means we have a coding error, but we can isolate the problem either by searching for instances of the variable in the code file, or by using the debugger. Slide 7.2.4 So what does a debugger do to help us find errors? Each programming language will have its own flavor of debugger; for an interpreted language like Scheme, the debugger actually places us inside the state of the computation. That is, when an error occurs, the debugger provides us access to the state of the computation at the time of the error, including access to the values of the variables within the computation. Moreover, we can step around inside the environment of the computation: we can work back up the chain of computational steps, examining what values were produced during reductions (where computation is reduced to a simpler expression), and examining what values were produced during substitutions (where the computation was converted to a simpler version of itself). Slide 7.2.5 For example, here is a simple procedure, which we have called with argument 2. Notice what happens when we hit the unbound variable error and enter the debugger. We are placed at the spot in the computation at which the error occurred. If we choose to step back through the chain of evaluations, we can see what expressions were reduced to get to this point, and what recursive versions of the same problem were invoked in reaching this stage. In this case, we note that foo was initially called with argument 2, and after a reduction through an if expression, we arrived at an expression that contained within it a simpler version of the same problem. This reduction stage repeated again, until we apparently reached the base case of the if expression, where we hit the unbound variable. We can see in this simple case that our unbound error is coming from within the body of foo and is in the base case of the decision process