forked from ascholerChemeketa/ThinkCPP2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdebugging_logic-errors.ptx
More file actions
293 lines (245 loc) · 11.2 KB
/
Copy pathdebugging_logic-errors.ptx
File metadata and controls
293 lines (245 loc) · 11.2 KB
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
<section xml:id="debugging_logic-errors">
<title>Logic Errors</title>
<subsection xml:id="debugging_my-program-doesnt-work">
<title>My program doesn't work</title>
<p>
Logic errors are hard to find because the compiler and interpreter provide no information about what is wrong.
Only you know what the program is supposed to do, and only you know that it isn't doing it.
</p>
<p>
The first step is to make a connection between the code and the behavior you get.
You need a hypothesis about what the program is actually doing.
Here are some questions to ask yourself:
<ul>
<li><p>Is there something the program was supposed to do that doesn't seem to be happening?
Find the section of the code that performs that function, and make sure it is executing when you think it should.
See <q>Flow of execution</q> on <xref ref="flowexec">page</xref>.</p></li>
<li><p>Is something happening that shouldn't?
Find code in your program that performs that function, and see if it is executing when it shouldn't.</p></li>
<li><p>Is a section of code producing an unexpected effect?
Make sure you understand the code, especially if it invokes methods in the Java library.
Read the documentation for those methods, and try them out with simple test cases.
They might not do what you think they do.</p></li>
</ul>
</p>
<p>
To program, you need a mental model of what your code does.
If it doesn't do what you expect, the problem might not actually be the program; it might be in your head.
</p>
<p>
<idx><h>mental model</h></idx>
The best way to correct your mental model is to break the program into components (usually the classes and methods) and test them independently.
Once you find the discrepancy between your model and reality, you can solve the problem.
</p>
<p>
Here are some common logic errors to check for:
</p>
<p>
<idx><h>logic error</h></idx>
<idx><h>error</h><h>logic</h></idx>
<ul>
<li><p>Remember that integer division always rounds toward zero.
If you want fractions, use <c>double</c>.
More generally, use integers for countable things and floating-point numbers for measurable things.</p></li>
<li><p>Floating-point numbers are only approximate, so don't rely on them to be perfectly accurate.
You should probably never use the <c>==</c> operator with <c>double</c>s.
Instead of writing <c>if (d == 1.23)</c>, do something like <c>if (Math.abs(d - 1.23) < .000001)</c>.</p></li>
<li><p>When you apply the equality operator (<c>==</c>) to objects, it checks whether they are identical.
If you meant to check equivalence, you should use the <c>equals</c> method instead.</p></li>
<li><p>By default for user-defined types, <c>equals</c> checks identity.
If you want a different notion of equivalence, you have to override it.</p></li>
<li><p>Inheritance can lead to subtle logic errors, because you can run inherited code without realizing it.
See <q>Flow of execution</q> on <xref ref="flowexec">page</xref>.</p></li>
</ul>
</p>
</subsection>
<subsection xml:id="debugging_ive-got-a-big-hairy-expression-it-doesnt-do-what-i-expect">
<title>I've got a big, hairy expression and it doesn't do what I expect</title>
<p>
<idx><h>expression</h><h>big and hairy</h></idx>
Writing complex expressions is fine as long as they are readable, but they can be hard to debug.
It is often a good idea to break a complex expression into a series of assignments to temporary variables:
</p>
<program>rect.translate((int) Math.round(0.5 * rect.getWidth()),
(int) Math.round(0.5 * rect.getHeight()));</program>
<p>
This example can be rewritten as follows:
</p>
<program>double halfWidth = 0.5 * rect.getWidth();
double halfHeight = 0.5 * rect.getHeight();
int dx = (int) Math.round(halfWidth);
int dy = (int) Math.round(halfHeight);
rect.translate(dx, dy);</program>
<p>
The second version is easier to read, partly because the variable names provide additional documentation.
It's also easier to debug, because you can check the types of the temporary variables and display their values.
</p>
<p>
<idx><h>temporary variable</h></idx>
<idx><h>variable</h><h>temporary</h></idx>
<idx><h>order of operations</h></idx>
<idx><h>precedence</h></idx>
Another problem that can occur with big expressions is that the order of operations may not be what you expect.
For example, to evaluate <m>\frac{x}{2 \pi}</m>, you might write this:
</p>
<program>double y = x / 2 * Math.PI;</program>
<p>
That is not correct, because multiplication and division have the same precedence, and they are evaluated from left to right.
This code computes <m>\frac{x}{2}\pi</m>.
</p>
<p>
If you are not sure of the order of operations, check the documentation, or use parentheses to make it explicit.
</p>
<program>double y = x / (2 * Math.PI);</program>
<p>
This version is correct, and more readable for other people who haven't memorized the order of operations.
</p>
</subsection>
<subsection xml:id="debugging_my-method-doesnt-return-what-i-expect">
<title>My method doesn't return what I expect</title>
<p>
<idx><h>return statement</h></idx>
<idx><h>statement</h><h>return</h></idx>
If you have a return statement with a complex expression, you don't have a chance to display the value before returning:
</p>
<program>public Rectangle intersection(Rectangle a, Rectangle b) {
return new Rectangle(
Math.min(a.x, b.x), Math.min(a.y, b.y),
Math.max(a.x + a.width, b.x + b.width)
- Math.min(a.x, b.x)
Math.max(a.y + a.height, b.y + b.height)
- Math.min(a.y, b.y));
}</program>
<p>
Instead of writing everything in one statement, use temporary variables:
</p>
<program>public Rectangle intersection(Rectangle a, Rectangle b) {
int x1 = Math.min(a.x, b.x);
int y1 = Math.min(a.y, b.y);
int x2 = Math.max(a.x + a.width, b.x + b.width);
int y2 = Math.max(a.y + a.height, b.y + b.height);
Rectangle rect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
return rect;
}</program>
<p>
Now you have the opportunity to display any of the intermediate variables before returning.
And by reusing <c>x1</c> and <c>y1</c>, you made the code smaller too.
</p>
</subsection>
<subsection xml:id="debugging_my-print-statement-isnt-doing-anything">
<title>My print statement isn't doing anything</title>
<p>
<idx><h>print statement</h></idx>
<idx><h>statement</h><h>print</h></idx>
If you use the <c>println</c> method, the output is displayed immediately, but if you use <c>print</c> (at least in some environments), the output gets stored without being displayed until the next newline.
If the program terminates without displaying a newline, you may never see the stored output.
If you suspect that this is happening, change some or all of the <c>print</c> statements to <c>println</c>.
</p>
</subsection>
<subsection xml:id="debugging_im-really-really-stuck-i-need-help">
<title>I'm really, really stuck and I need help</title>
<p>
First, get away from the computer for a few minutes.
Computers emit waves that affect the brain, causing the following symptoms:
<ul>
<li><p>Frustration and rage.</p></li>
<li><p>Superstitious beliefs (<q>the computer hates me</q>) and magical thinking (<q>the program works only when I wear my hat backward</q>).</p></li>
<li><p>Sour grapes (<q>this program is lame anyway</q>).</p></li>
</ul>
</p>
<p>
If you suffer from any of these symptoms, get up and go for a walk.
When you are calm, think about the program.
What is it doing?
What are possible causes of that behavior?
When was the last time you had a working program, and what did you do next?
</p>
<p>
Sometimes it just takes time to find a bug.
People often find bugs when they let their mind wander.
Good places to find bugs are buses, showers, and bed.
</p>
</subsection>
<subsection xml:id="debugging_no-i-really-need-help">
<title>No, I really need help</title>
<p>
It happens.
Even the best programmers get stuck.
Sometimes you need another pair of eyes.
Before you bring someone else in, make sure you have tried the techniques described in this appendix.
</p>
<p>
Your program should be as simple as possible, and you should be working on the smallest input that causes the error.
You should have print statements in the appropriate places (and the output they produce should be comprehensible).
You should understand the problem well enough to describe it concisely.
</p>
<p>
When you bring someone in to help, give them the information they need:
<ul>
<li><p>What kind of bug is it?
Compile-time, run-time, or logic?</p></li>
<li><p>What was the last thing you did before this error occurred?
What were the last lines of code that you wrote, or what is the test case that fails?</p></li>
<li><p>If the bug occurs at compile time or run time, what is the error message, and what part of the program does it indicate?</p></li>
<li><p>What have you tried, and what have you learned?</p></li>
</ul>
</p>
<p>
By the time you explain the problem to someone, you might see the answer.
This phenomenon is so common that some people recommend a debugging technique called <q>rubber ducking</q>.
Here's how it works:
</p>
<p>
<idx><h>rubber duck</h></idx>
<idx><h>debugging</h><h>rubber duck</h></idx>
<ol>
<li><p>Buy a standard-issue rubber duck.</p></li>
<li><p>When you are really stuck on a problem, put the rubber duck on the desk in front of you and say, <q>Rubber duck, I am stuck on a problem.
Here's what's happening<ellipsis /></q></p></li>
<li><p>Explain the problem to the rubber duck.</p></li>
<li><p>Discover the solution.</p></li>
<li><p>Thank the rubber duck.</p></li>
</ol>
</p>
<p>
We're not kidding, it works!
See \url{https://en.wikipedia.org/wiki/Rubber_duck_debugging}.
</p>
</subsection>
<subsection xml:id="debugging_i-found-bug">
<title>I found the bug!</title>
<p>
When you find the bug, the way to fix it is usually obvious.
But not always.
Sometimes what seems to be a bug is really an indication that you don't understand the program, or your algorithm contains an error.
In these cases, you might have to rethink the algorithm or adjust your mental model.
Take some time away from the computer to think, work through test cases by hand, or draw diagrams to represent the computation.
</p>
<p>
After you fix the bug, don't just start in making new errors.
Take a minute to think about what kind of bug it was, why you made the error, how the error manifested itself, and what you could have done to find it faster.
Next time you see something similar, you will be able to find the bug more quickly.
Or even better, you will learn to avoid that type of bug for good.
</p>
<p>
\ifplastex
\else
</p>
<p>
{ About the Authors}
</p>
<p>
<em>Allen Downey</em> is a professor of computer science at Olin College of Engineering.
He has taught computer science at Wellesley College, Colby College, and UC Berkeley.
He has a PhD in computer science from UC Berkeley and master's and bachelor's degrees from MIT.
Allen is the creator of the best-selling Think series for O'Reilly, which includes <em>Think Python</em>, <em>Think Complexity</em>, <em>Think DSP</em>, and <em>Think Bayes</em>.
</p>
<p>
<em>Chris Mayfield</em> is an associate professor of computer science at James Madison University, with a research focus on CS education and professional development.
He has a PhD in computer science from Purdue University and bachelor's degrees in CS and German from the University of Utah.
</p>
<p>
\fi
</p>
</subsection>
</section>