diff options
author | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
commit | 562a9a52d599d9a05f871404050968a5fd282640 (patch) | |
tree | 7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/v1ch15 | |
parent | 5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff) | |
download | tour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz |
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/v1ch15')
-rw-r--r-- | js/games/nluqo.github.io/~bh/v1ch15/debug.html | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/v1ch15/debug.html b/js/games/nluqo.github.io/~bh/v1ch15/debug.html new file mode 100644 index 0000000..f5f31c0 --- /dev/null +++ b/js/games/nluqo.github.io/~bh/v1ch15/debug.html @@ -0,0 +1,713 @@ +<HTML> +<HEAD> +<TITLE>Computer Science Logo Style vol 1 ch 15: Debugging</TITLE> +</HEAD> +<BODY> +<CITE>Computer Science Logo Style</CITE> volume 1: +<CITE>Symbolic Computing</CITE> 2/e Copyright (C) 1997 MIT +<H1>Debugging</H1> + +<TABLE width="100%"><TR><TD> +<IMG SRC="../csls1.jpg" ALT="cover photo"> +<TD><TABLE> +<TR><TD align="right"><CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian +Harvey</A><BR>University of California, Berkeley</CITE> +<TR><TD align="right"><BR> +<TR><TD align="right"><A HREF="../pdf/v1ch15.pdf">Download PDF version</A> +<TR><TD align="right"><A HREF="../v1-toc2.html">Back to Table of Contents</A> +<TR><TD align="right"><A HREF="../v1ch14/v1ch14.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../v1ch16/versions.html"><STRONG>NEXT</STRONG></A> +<TR><TD align="right"><A HREF="https://mitpress.mit.edu/books/computer-science-logo-style-second-edition-volume-1">MIT +Press web page for Computer Science Logo Style</A> +</TABLE></TABLE> + +<HR> + + +<P> +I haven't talked much, until now, about how to find and fix mistakes +in the programs you write. Except for the chapter-length examples +in Chapters 6, 12, and 14, it hasn't been much of a +problem because the sample programs I've shown you have been so +small. That doesn't mean you can't make a mistake in a small program! +But mistakes are relatively easy to find when the entire program is +one procedure with just a few instruction lines. In a real +programming project, which might have 20 or 200 procedures, it's harder +to locate an error. + +<P><H2>Using Error Messages</H2> + +<P>At one point in Chapter 13 I saw the error message + +<P><PRE>I don't know how to one in pokerhand +</PRE> + +<P>Logo's error messages were deliberately designed to use an +informal, smooth, low-key style so that beginning programmers won't +find them intimidating. But there is a lot of information in that +message if you learn how to find it. The message tells me three +things. First, it tells me what <EM>kind</EM> of error is involved. +In this particular message, +the phrase "I don't know how" suggests that a procedure is missing, +and the words "to one" subtly suggest how the problem could be fixed. +Second, the message tells me the <EM>specific</EM> expression that was +in error: the word <CODE>one</CODE>. Third, it tells me that the error was +detected while Logo was carrying out the procedure named <CODE> +pokerhand</CODE>. + +<P>The precise form of the message may be different in different situations. +If you make a mistake in a top-level instruction (that is, one that you type +to a question mark prompt, not inside a procedure), the part about <CODE>in +pokerhand</CODE> won't be included. + +<P>One very important thing to remember is that the place where an error +is <EM>found</EM> may not be the place where the error really <EM> +is.</EM> That's a little vague, so let's think about the <CODE>I don't know +how</CODE> error. All the Logo interpreter knows is that it has been +asked to invoke a procedure that doesn't exist. But there can be +several possible reasons for that. The most common reason is that +you've just misspelled the name of a procedure. When the message is + +<P><PRE>I don't know how to forwrad in poly +</PRE> + +<P>you can be pretty sure, just from reading the message, that +the problem is a misspelling of <CODE>forward</CODE>. In this case the +mistake is in <CODE>poly</CODE>, just as the message tells you. + +<P>On the other hand you might get a message like this about a procedure +that really should exist. For example, I might have seen + +<P><PRE>I don't know how to straight in pokerhand +</PRE> + +<P>If I had been confronted with that message, I might have +looked at <CODE>pokerhand</CODE>, and indeed I would have found an +instruction that invokes a procedure named <CODE>straight</CODE>. But +that's not an error; there <EM>should</EM> be such a procedure. One of +two things would be wrong: either I'd forgotten to define <CODE> +straight</CODE> altogether or else I made a spelling mistake in the title +line of <CODE>straight</CODE> rather than in an instruction line of <CODE> +pokerhand</CODE>. To find out, I would type the command <CODE>pots</CODE> (which, +as you recall, stands for Print Out TitleS) and look for a possible +misspelling of <CODE>straight</CODE>. + +<P>Another way to get the same error message is to write a program using one +version of Logo and then transfer it to another version with somewhat +different primitives. For example, Berkeley Logo includes higher order +functions such as <CODE>map</CODE> that are not primitive in most other Logo +dialects. If you write a program that uses <CODE>map</CODE> and then try to run it +in another version of Logo, you'll get a message saying <CODE>I don't know +how to map</CODE>. In that case you'd have to write your own version of <CODE> +map</CODE> or rewrite the program to avoid using it--for example, by using a +recursive operation instead. + +<P>The mistake I actually made in Chapter 13 wasn't a misspelling, a +missing definition, or a nonexistent primitive. Instead, I failed to +quote a list with square brackets. The particular context in which I +did it, in an input to <CODE>ifelse</CODE>, is a fairly obscure one. But +here is a common beginner's mistake, especially for people who are +accustomed to other programming languages: + +<P><PRE>? <U>print "How are you?"</U> +How +i don't know how to are +</PRE> + +<P>The moral of all this is that the error message <EM>does</EM> give you +some valuable help in finding your bug, but it <EM>doesn't</EM> tell +you the whole story. You have to read the message intelligently. + +<P><H2>Invalid Data</H2> + +<P>I've spent a lot of time on the <CODE>I don't know how</CODE> message because +it's probably the most common one. Another very common kind of +message, which will merit some analysis here, is + +<P><PRE><U>procedure</U> doesn't like <U>datum</U> as input +</PRE> + +<P>In general, this means that you've violated the rules about +the kinds of data that some primitive procedure requires as +input. (Recall that the type of input is one of the things I've been +insisting that you mention as part of the description of a procedure.) +For example, <CODE>word</CODE> requires words as inputs, so: + +<P><PRE>? <U>print word "hello, [old buddy]</U> +word doesn't like [old buddy] as input +</PRE> + +<P>There are several special cases, however, that come up more often +than something as foolish as using a list as an input to <CODE>word</CODE>. +The most common message of this form is this one: + +<P><PRE>butfirst doesn't like [] as input +</PRE> + +<P>This almost invariably means that you've left out the +stop rule in a recursive +procedure. The offending input to <CODE>butfirst</CODE> isn't +an explicit empty list but instead is the result of evaluating +a variable, usually an input to the +procedure you're writing, that's <CODE>butfirst</CODE>ed in the recursive +invocation. This is a case where the error isn't really in the +instruction that caused the message. Usually there is nothing wrong +with the actual invocation of <CODE>butfirst</CODE>; the error is a missing +instruction earlier in the procedure. If the input is a +word instead of a list, this message will take the possibly confusing +form + +<P><PRE>butfirst doesn't like as input +</PRE> + +<P>That's an invisible empty word between <CODE>like</CODE> and <CODE> +as</CODE>! + +<P>I said that this message is almost always caused by a missing stop +rule. You have to be careful about the "almost." For example, +recall this practical joke procedure from Chapter 1: + +<P><PRE>to process :instruction +test emptyp :instruction +iftrue [type "|? | process readlist stop] +iffalse [print sentence [|I don't know how to|] first :instruction] +end +</PRE> + +<P>This is not a recursive procedure, and the question of stop +rules doesn't arise. But its input might be empty, because the victim +enters a blank line. If I hadn't thought of that, and had written + +<P><PRE>to process :instruction +print sentence [|I don't know how to|] first :instruction +end +</PRE> + +<P>the result would be + +<P><PRE>first doesn't like [] as input in process +</PRE> + +<P>Another case that sometimes comes up in programs that do arithmetic is + +<P><PRE>/ doesn't like 0 as input +</PRE> + +<P>For example, if you write a program that takes the average +of a bunch of numbers and you try to use the program with an empty +list of numbers as input, you'll end up trying to divide zero by +zero. The solution is to insert an instruction that explicitly tests +for that possibility. + +<P>As always, the procedure that provokes the error message may not +actually be the procedure that is in error. Consider this short +program: + +<P><PRE>to second :thing +output first butfirst :thing +end + +to swap :list +output list (second :list) (first :list) +end + +? <U>print swap [watch pocket]</U> +pocket watch +? <U>print swap [farewell]</U> +first doesn't like [] as input in second +[output first butfirst :thing] +</PRE> + +<P>Although the error was caught during the invocation of <CODE> +second</CODE>, there is nothing wrong with <CODE>second</CODE> itself. The error +was in the top-level instruction, which provided a bad input to <CODE> +swap</CODE>. That instruction doesn't even include an explicit reference to +<CODE>second</CODE>. In this small example it's easy to see what happened. But +in a more complicated program it can be hard to find errors like this +one. + +<P>There are two ways you can protect yourself against this kind of +difficulty. The first is <EM>defensive programming.</EM> I could have +written the program this way: + +<P><PRE>to swap :list +if emptyp :list [pr [empty input to swap] stop] +if emptyp butfirst :list [pr [singleton input to swap] stop] +output list (second :list) (first :list) +end +</PRE> + +<P>This version checks for bad inputs and gives a more helpful +error message.*Actually, when you invoke this version of +<CODE>swap</CODE> with a bad input, you'll see <EM>two</EM> error messages. +The procedure itself will print an error message. Then, since it <CODE> +stop</CODE>s instead of <CODE>output</CODE>ting something to its superprocedure, +you'll get a <CODE>didn't output</CODE> error message from the Logo +interpreter. It would also be possible to figure out an appropriate +output for these cases and not consider them errors at all: + +<P><PRE>to swap :list +if emptyp :list [output []] +if emptyp butfirst :list [output :list] +output list (second :list) (first :list) +end +</PRE> + +<P>This version manages to produce an output for any input at +all. How should you choose between these two defensively written +versions? It depends on the context in which you'll be using <CODE> +swap</CODE>. If you are writing a program in which <CODE>swap</CODE> should always +get a particular kind of list as input, which should always have two +members, then you should use the first defensive version, which will +let you know if you make an error in the input to <CODE>swap</CODE>. But if +<CODE>swap</CODE> is intended as a general tool, which might be used in a +variety of situations, it might be better to accept any input. + +<P>The second protective technique, besides defensive programming, is +tracing, the technique we used in Chapter 9. If you get an +error message from a utility procedure like <CODE>second</CODE> and you have no +idea how it was invoked, you can find out by tracing the entry into all of +your procedures. + +<P>Another way to get the <CODE>doesn't like</CODE> message is to forget the +order of inputs to a procedure, either a primitive or one that you've +written. For example, <CODE>lput</CODE> is a primitive operation that +requires two inputs. The first input can be any datum, but the +second must be a list. The output from <CODE>lput</CODE> is a list that +contains all the members of the second input, plus one more member at +the end equal to the first input. + +<P><PRE>? <U>show lput "c [a b]</U> +[a b c] +</PRE> + +<P><CODE>Lput</CODE> takes its inputs in the same order as <CODE>fput</CODE>, +with the new member first and then the old list. But you might get +confused and want the inputs to appear left-to-right as they appear in +the result: + +<P><PRE>? <U>show lput [a b] "c</U> +lput doesn't like c as input +</PRE> + +<P><H2>Incorrect Results</H2> + +<P>Beginning programmers are often dismayed when they see an error +message, but more experienced programmers are relieved. They know +that the bugs that cause such messages are the easy ones to find! Much +harder are the bugs that allow a program to run to completion but +produce the wrong answer. In that kind of situation you don't have +the advantage of knowing which procedure tickled the error message, so +it's hard to know where to begin looking. + +<P>Here's a short program with a couple of bugs in it. <CODE>Arabic</CODE> is an +operation that takes one input, a word that is a Roman numeral. +The output from <CODE>arabic</CODE> is the number represented by that Roman numeral +in ordinary (Arabic numeral) notation. + +<P><PRE>to arabic :num +output addup map "digit :num +end + +to digit :digit +output lookup :digit [[I 1] [V 5] [X 10] [L 50] [C 100] [D 500] [M 1000]] +end + +to lookup :word :dictionary +if emptyp :dictionary [output "] +if equalp :word first first :dictionary [output last first :dictionary] +output lookup :word bf :dictionary +end + +to addup :list +if emptyp :list [output 0] +if emptyp bf :list [output first :list] +if (first :list) < (first bf :list) ~ + [output sum ((first bl :list)-(first :list)) addup bf bf :list] +output sum first :list addup bf :list +end +</PRE> + +<P><CODE>Arabic</CODE> uses two non-primitive subprocedures, dividing its +task into two parts. First <CODE>digit</CODE> translates each letter of the Roman +numeral into the number it represents: <CODE>C</CODE> into 100, <CODE>M</CODE> into 1000. +The result is a list of numbers. Then <CODE>addup</CODE> translates that list into +a single number, adding or subtracting each member as appropriate. The rule +is that the numbers are added, except that a smaller number that appears to +the left of a larger one is subtracted from the total. For example, in the +Roman numeral <CODE>CLIV</CODE> all the letters are added except for the <CODE>I</CODE>, +which is to the left of the <CODE>V</CODE>. Since <CODE>I</CODE> represents 1 and +<CODE>V</CODE> represents 5, and 1 is less than 5, the <CODE>I</CODE> is subtracted. The +result is 100+50+5-1 or 154. + +<P>Here's what happened the first time I tried <CODE>arabic</CODE>: + +<P><PRE>? <U>print arabic "MLXVI</U> +13 +</PRE> + +<P>This is a short enough program that you may be able to find the bug +just by reading it. But even if you do, let's pretend that you don't, +because I want to use this example to talk about some ways of looking +for bugs systematically. + +<P>The overall structure of the program is that <CODE>digit</CODE> is invoked for each +letter, and the combined output from all the calls to <CODE>digit</CODE> is used as +the input to <CODE>addup</CODE>. The first step is to try to figure out which of +the two is at fault. Which should we try first? Since <CODE>addup</CODE> depends +on the work of <CODE>digit</CODE>, whereas <CODE>digit</CODE> doesn't depend on <CODE> +addup</CODE>, it's probably best to start with <CODE>digit</CODE>. So let's try looking +at the output from <CODE>digit</CODE> directly. + +<P><PRE>? <U>print digit "M</U> +1000 +? <U>print digit "V</U> +5 +</PRE> + +<P>So far so good. Perhaps the problem is in the way <CODE>map</CODE> is +used to combine the results from <CODE>digit</CODE>: + +<P><PRE>? <U>show map "digit "MLXVI</U> +1000501051 +</PRE> + +<P>Aha! I wanted a list of numbers, one for each Roman digit, +but instead I got all the numbers combined into one long word. I had +momentarily forgotten that if the second input to <CODE>map</CODE> is a word, +its output will be a word also. As soon as I see this, the solution +is apparent to me: I should use <CODE>map.se</CODE> instead of <CODE>map</CODE>. + +<P><PRE>? <U>show map.se "digit "MLXVI</U> +[1000 50 10 5 1] + +to arabic :num +output addup map.se "digit :num +end + +? <U>print arabic "MLXVI</U> +1066 +</PRE> + +<P>This time I got the answer I expected. On to more +test cases: + +<P><PRE>? <U>print arabic "III</U> +3 +? <U>print arabic "XVII</U> +17 +? <U>print arabic "CLV</U> +155 +? <U>print arabic "CLIV</U> +150 +? +</PRE> + +<P>Another error! The result was 150 instead of the correct 154. +Since the other three examples are correct, the program is not +completely at sea; it's a good guess that the bug has to do with the +case of subtracting instead of adding. Trying a few more examples +will help confirm that guess. + +<P><PRE>? <U>print arabic "IV</U> +0 +? <U>print arabic "MCM</U> +1000 +? <U>print arabic "MCMLXXXIV</U> +1080 +? <U>print arabic "MDCCLXXVI</U> +1776 +? +</PRE> + +<P>Indeed, numbers that involve subtraction seem to fail, +while ones that are purely additive seem to work. If you look +carefully at exactly <EM>how</EM> the program fails, you may notice +that the letter that should be subtracted and the one after it are +just ignored. So in the numeral <CODE>MCMLXXXIV</CODE>, which represents 1984, the +<CODE>CM</CODE> and the <CODE>IV</CODE> don't contribute to the program's result. + +<P>Once again, we must find out whether the bug is in <CODE>digit</CODE> or in <CODE> +addup</CODE>, and it makes sense to start by checking the one that's called first. +(If you read the instructions in the definitions of <CODE>digit</CODE> and <CODE> +addup</CODE>, you'll see that <CODE>digit</CODE> handles each digit in isolation, whereas +<CODE>addup</CODE> is the one that looks at two consecutive digits to decide whether +or not to subtract. But at first I'm not reading the instructions at all; +I'm trying to be sure that I understand the <EM>behavior</EM> of each +procedure before I look inside any of them. For a simple problem like this +one, the approach I'm using is more ponderous than necessary. But it would +pay off for a larger program with more subtle bugs.) + +<P><PRE>? <U>show map.se "digit "VII</U> +[5 1 1] +? <U>show map.se "digit "MDCCLXXVI</U> +[1000 500 100 100 50 10 10 5 1] +</PRE> + +<P>I've started with Roman numerals for which the overall program +works. Why not just concentrate on the cases that fail? Because I want to +see what the <EM>correct</EM> output from <CODE>map</CODE>ping <CODE>digit</CODE> over the +Roman numeral is supposed to look like. It turns out to be a list of +numbers, one for each letter in the Roman numeral. + +<P>You may wonder why I need to investigate the correct behavior of <CODE> +digit</CODE> experimentally. If I've planned the program properly in the +first place, I should <EM>know</EM> what it's supposed to do. There +are several reasons why I might feel a need for this sort of +experiment. Perhaps it's someone else's program I'm debugging, and I +don't know what the plan was. Perhaps it's a program I wrote a long +time ago and I've forgotten. Finally, since there is a bug after +all, perhaps my understanding is faulty even if I do think I know what +<CODE>digit</CODE> is supposed to do. + +<P>Now let's try <CODE>digit</CODE> for some of the buggy cases. + +<P><PRE>? <U>show map.se "digit "IV</U> +[1 5] +? <U>show map.se "digit "MCMLXXXIV</U> +[1000 100 1000 50 10 10 10 1 5] +? +</PRE> + +<P><CODE>Digit</CODE> still does the right thing: It outputs the number +corresponding to each letter. The problem must be in <CODE>addup</CODE>. + +<P>Now it's time to take a look at <CODE>addup</CODE>. There are four +instructions in its definition. Which is at fault? It must be one +that comes into play only for the cases in which subtraction is +needed. That's a clue that it will be one of the <CODE>if</CODE> +instructions, although instructions that aren't explicitly +conditional can, in fact, depend on earlier <CODE>if</CODE> tests. (In this +procedure, for example, the last instruction doesn't look +conditional. But it is carried out only if none of the earlier +instructions results in an <CODE>output</CODE> being evaluated.) + +<P>Rather than read every word of every line carefully, we should start +by knowing the purpose of each instruction. The first one is an end +test, detecting an empty numeral. The second is also an end test, +detecting a single-digit numeral. (Why are two end tests necessary? +How would the program fail if each one were eliminated?) The third +instruction deals with the subtraction case, and the fourth with the +addition case. The bug, then, is probably in the third instruction. +Here it is again: + +<P><PRE>if (first :list) < (first bf :list) ~ + [output sum ((first bl :list)-(first :list)) addup bf bf :list] +</PRE> + +<P>At this point a careful reading of the instruction will +probably make the error obvious. If not, look at each of the +expressions used within the instruction, like + +<P><PRE>first :list +</PRE> + +<P>and + +<P><PRE>bf bf :list +</PRE> + +<P>What number or list does each of them represent? + +<P>(If you'd like to take time out for a short programming project now, +you might try writing <CODE>roman</CODE>, an operation to translate in the +opposite direction, from Arabic to Roman numerals. The rules are that +<CODE>I</CODE> can be subtracted from <CODE>V</CODE> or <CODE>X</CODE>; <CODE>X</CODE> can be +subtracted from <CODE>L</CODE> or <CODE>C</CODE>; and <CODE>C</CODE> can be subtracted from +<CODE>D</CODE> or <CODE>M</CODE>. You should never need to repeat any symbol more +than three times. For example, you should use <CODE>IV</CODE> rather than +<CODE>IIII</CODE>.) + +<P><H2>Tracing and Stepping</H2> + +<P>In Chapter 9 we used the techniques of <EM>tracing</EM> and <EM> +stepping</EM> to help you understand how recursive procedures work. The +same techniques can be very valuable in debugging. Tracing a +procedure means making it print an indication of when it starts and +stops. Stepping a procedure means making it print each of its +instructions and waiting for you to type something before evaluating +the instruction. + +<P>Berkeley Logo provides primitive commands <CODE>trace</CODE> and +<CODE>step</CODE> that automatically trace or step procedures for you. <CODE> +Trace</CODE> and <CODE>step</CODE> take one input, which can be either a word or a +list. If the input is a word, it must be the name of a procedure. If +a list, it must be a list of words, each of which is the name of a +procedure. The effect of <CODE>trace</CODE> is to modify the procedure or +procedures named in the input to identify the procedure and its inputs +when it is invoked. The effect of <CODE>step</CODE> is to modify the named +procedure(s) so that each instruction is printed before being +evaluated. + +<P>Tracing a procedure is particularly useful in the annoying situation in +which a program just sits there forever, never stopping, but never printing +anything either. This usually means that there is an error in a recursive +procedure, which invokes itself repeatedly with no stop rule or with an +ineffective one. If you trace recursive procedures, you can find out how +you got into that situation. + +<P> +<H2>Pausing</H2> + +<P>When a program fails, either with an error message or by printing the +wrong result, it can be helpful to examine the values of the variables +used within the program. Of course, you understand by now that "the +variables used within the program" may be a complicated idea; if +there are recursive procedures with local variables, there may be +several variables with the same name, one for each invocation of a +procedure. + +<P>Once a program is finished running, the local variables created by the +procedures within the program no longer exist. You can examine global +variables individually by <CODE>print</CODE>ing their values or all at +once with the <CODE>pons</CODE> command. (<CODE>Pons</CODE> stands for Print Out +NameS; it takes no inputs and prints the names and values of +all current variables.) But it's too late to examine local variables +after a program stops. + +<P>To get around this problem, Berkeley Logo provides +a <CODE>pause</CODE> command. This command takes +no inputs. Its effect is to stop, +temporarily, the procedure in which it appears. (Like <CODE>stop</CODE> and +<CODE>output</CODE>, <CODE>pause</CODE> is meaningless at top level.) Logo prints a +question mark prompt (along with the name of the paused procedure +to remind you that it's paused), and you can enter instructions to be evaluated +as usual. But the paused procedure is <EM>still active;</EM> its local +variables still exist. (Any superprocedures of the paused procedure, +naturally, are also still active.) The instructions you type while the +procedure is paused can make use of local variables, just as if the +instructions appeared within the procedure definition. + +<P>The main use of <CODE>pause</CODE> is for debugging. If your program dies +with an error message you don't understand, you can insert a <CODE> +pause</CODE> command just before the instruction that gets the error. Then +you can examine the variables that will be used by that instruction. + +<P>Better yet, you can ask Logo to pause <EM>automatically</EM> whenever +an error occurs. In fact, you can ask Logo to carry out any instructions +you want, whenever an error occurs, by creating a variable named <CODE>erract</CODE> +(short for error action) whose value is an instruction list. If you want +your program to pause at any error, say + +<P><PRE>? <U>make "erract [pause]</U> +</PRE> + +<P>before you run the program. To undo this request, you can +erase the variable name <CODE>erract</CODE> with the <CODE>ern</CODE> (erase name) +command: + +<P><PRE>? <U>ern "erract</U> +</PRE> + +<P>Once you've examined the relevant variables, you may want to continue +running the program. You'll certainly want to continue if this pause +wasn't the one you're waiting for, just before the error happens. +Logo provides the command <CODE>continue</CODE> (abbreviated <CODE>co</CODE>) for this +purpose. If you type <CODE>continue</CODE> with no input, Logo will continue the +evaluation of the paused procedure where it left off. + +<P>It is also +possible to use <CODE>continue</CODE> with an input, turning the <CODE>pause</CODE> +command into an operation by providing a value for it to output. Whether +or not that's appropriate depends on which error message you get. If +the message complains about a missing value, you may be able to provide +one to allow the program to continue: + +<P><PRE>to demo.error +print first :nonesuch +end + +? <U>make "erract [pause]</U> +? <U>demo.error</U> +nonesuch has no value in demo.error +[print first :nonesuch] +Pausing... +demo.error? <U>continue "hello</U> +h +</PRE> + +<P>If, after examining variables, you figure out the reason for the bug, +you may not want to bother continuing the buggy procedure. Instead +you'll want to forget about it, edit the definition to fix the bug, +and try again. But you shouldn't just forget about it because the +procedure is still active. If you don't want to continue it, you +should <CODE>stop</CODE> it instead, to get back to the "real" top level +with no procedures active. (Instead of <CODE>stop</CODE>, a more definitive +way to stop all active procedures is with the instruction + +<P><PRE>throw "toplevel +</PRE> + +<P>For now just think of this as a magic incantation; we'll talk more +about <CODE>throw</CODE> in the second volume.) + +<P>Berkeley Logo also has a special character +that you can type on the keyboard to cause an immediate pause. The +character depends on which computer you're using; see Appendix A. +This is not as useful a capability as +you might think because it's hard to synchronize your typing with the +activity of the program so that it gets paused in the right <EM> +context</EM> (that is, with the right procedures active and the right +local variables available). But it can be useful if you can see that +the program is repeating the same activities over and over, for +example; pausing just about anywhere during that kind of <EM>loop</EM> +is likely to give you useful information. + +<P><H2>Final Words of Wisdom</H2> + +<P>You may be feeling a frustrating sense of incompleteness about this +chapter. After the chapter on variables, for example, you really knew +everything there is to know about variables. (I suppose that's not +strictly true, since you hadn't thought about recursion yet, but it's +true enough.) But you certainly don't know everything there is to know +about debugging. That's because there isn't a complete set of rules +that will get you through every situation. You just have to do a lot +of programming, meet a lot of bugs, and develop an instinct for them. + +<P>As a beginner, you'll probably meet bugs with a different flavor from +the ones I've been discussing. You'll put a space after a quotation +mark or a colon, before the word to which it should be attached. +You'll leave out a left or right parenthesis or bracket. (Perhaps +you'll get confused about when to use parentheses and when brackets!) +All of these simple errors will quickly get you error messages, and +you can probably find your mistake just by reading the offending +instruction. Later, as your programs get more complicated, you'll +start having the more interesting bugs that require analysis to find +and fix. + +<P>It's a good idea to program with a partner. Sometimes you can find +someone else's bugs more easily than your own--when you read +your own program, you know too well what you <EM>meant</EM> to say. +This advice is not just for beginners; even experienced programmers +often benefit from sharing their bugs with a friend. Another +advantage of such a partnership is that trying to explain your program +to someone else will often help you understand it more clearly +yourself. I've often discovered a persistent bug halfway through +explaining the problem to someone. + +<P>The main point, I think, is one I've made in earlier chapters: there +is nothing shameful about a bug in your program. As a teacher, I've +been astonished to see students react to a simple bug by angrily +erasing an entire program, which they'd spent hours writing! Teach +yourself to expect bugs and approach them with a good-natured spirit. + +<P>On the other hand, you can minimize your debugging time by writing the +program in a reasonable style in the first place. If your program is +one long procedure, you should know that you're making it harder to +locate an offending instruction. If all your variables are named <CODE> +x</CODE> and <CODE>y</CODE>, you deserve whatever happens to you! And if you can't +figure out, yourself, which procedure does what, then perhaps you +should stop typing in procedures and spend a little time with paper +and pencil listing the tasks each procedure needs to carry out. + +<P><A HREF="../v1-toc2.html">(back to Table of Contents)</A> +<P><A HREF="../v1ch14/v1ch14.html"><STRONG>BACK</STRONG></A> +chapter thread <A HREF="../v1ch16/versions.html"><STRONG>NEXT</STRONG></A> + +<P> +<ADDRESS> +<A HREF="../index.html">Brian Harvey</A>, +<CODE>bh@cs.berkeley.edu</CODE> +</ADDRESS> +</BODY> +</HTML> |