about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/v1ch15/debug.html
blob: f5f31c03023b2127da531ba88417b221630a9f49 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
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 &quot;I don't know how&quot; suggests that a procedure is missing,
and the words &quot;to one&quot; 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 &quot;How are you?&quot;</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 &quot;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 &quot;almost.&quot; For example,
recall this practical joke procedure from Chapter 1:

<P><PRE>to process :instruction
test emptyp :instruction
iftrue [type &quot;|? | 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 &quot;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] &quot;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 &quot;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 &quot;]
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) &lt; (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 &quot;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 &quot;M</U>
1000
? <U>print digit &quot;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 &quot;digit &quot;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 &quot;digit &quot;MLXVI</U>
[1000 50 10 5 1]

to arabic :num
output addup map.se &quot;digit :num
end

? <U>print arabic &quot;MLXVI</U>
1066
</PRE>

<P>This time I got the answer I expected.  On to more
test cases:

<P><PRE>? <U>print arabic &quot;III</U>
3
? <U>print arabic &quot;XVII</U>
17
? <U>print arabic &quot;CLV</U>
155
? <U>print arabic &quot;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 &quot;IV</U>
0
? <U>print arabic &quot;MCM</U>
1000
? <U>print arabic &quot;MCMLXXXIV</U>
1080
? <U>print arabic &quot;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 &quot;digit &quot;VII</U>
[5 1 1]
? <U>show map.se &quot;digit &quot;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 &quot;digit &quot;IV</U>
[1 5]
? <U>show map.se &quot;digit &quot;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) &lt; (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 &quot;the
variables used within the program&quot; 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 &quot;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 &quot;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 &quot;erract [pause]</U>
? <U>demo.error</U>
nonesuch has no value  in demo.error
[print first :nonesuch]
Pausing...
demo.error? <U>continue &quot;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 &quot;real&quot; 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 &quot;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>