-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnotes.Rmd
297 lines (201 loc) · 5.82 KB
/
notes.Rmd
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
---
title: "Stat 33A - Lecture Notes 12"
date: Nov 8, 2020
output: pdf_document
---
While-loops
===========
A while-loop runs a block of code repeatedly while some condition is `TRUE`.
The condition is checked before each iteration.
For example, suppose you want to add up numbers from 0 to 50, but stop as soon
as the total is greater than 50:
```{r}
numbers = seq(0, 50)
total = 0
i = 1
while (total < 50) {
total = total + numbers[i]
cat(sprintf("i is %i, total is %i\n", i, total))
i = i + 1
}
total
i
```
Use a while-loop when you don't know how many iterations are needed until after
the loop runs.
While-loops are a generalization of for-loops.
That means you can write a for-loop as a while-loop:
```{r}
x = c(5, 0, -1, 10)
for (i in seq_along(x)) {
cat(x[i], "\n")
}
i = 1
while (i <= length(x)) {
cat(x[i], "\n")
i = i + 1
}
```
But generally you shouldn't do this!
Developing Iterative Code
=========================
See the slides.
When thinking about writing a loop, try (in order):
1. vectorization
2. apply functions
* Try an apply function if iterations are independent.
3. for/while-loops
* Try a for-loop if some iterations depend on others.
* Try a while-loop if the number of iterations is unknown.
4. recursion
* Convenient for naturally recursive problems (like Fibonacci),
but often there are faster solutions.
Before you write the loop, try writing the code for just 1 iteration.
Make sure that code works; it's easy to test code for 1 iteration.
When you have 1 iteration working, then try using the code in a loop
(you will have to make some small changes).
If your loop doesn't work, try to figure out which iteration is
causing the problem. One way to do this is to use message() to print
out information.
Then try to write the code for the broken iteration, get that
iteration working, and repeat this whole process.
Messages, Warnings, and Errors
==============================
The `message`, `warning`, and `stop` functions form R's _condition system_.
The condition system provides a way to report and handle unusual conditions.
The `message` function prints message to the R console:
```{r}
message("Hello")
```
The `warning` function prints a warning:
```{r}
if (c(TRUE, FALSE)) 42
warning("Hi")
```
By default, calling `warning` in a function prints the name of the function:
```{r}
f = function(x, y) {
warning("This is a warning!")
x + y
}
f(3, 4)
```
You can use the `call.` parameter to control this:
```{r}
f = function(x, y) {
warning("This is a warning!", call. = FALSE)
x + y
}
f(3, 4)
```
The `stop` function prints an error message and stops evaluation:
```{r}
stop("Hi")
f = function(x, y) {
stop("This is an error!")
x + y
}
f(3, 4)
```
The `stop` function also has a `call.` parameter.
Preventing Bugs
===============
Modularity
----------
Try to break problems into smaller steps.
Write down the input(s) and output(s) for each step.
Create functions for steps that are reusable. Use parameters for input and the
return value for output.
Be wary of extremely short (1 line) or long (> 20 line) functions. Often a sign
of failure to break down the problem.
Code Clarity
------------
Format your code so that it's easy to read:
* Use whitespace:
+ Put spaces after commas and around binary operators.
+ Indent code inside curly braces `{ }` by 2 spaces or 1 tab.
+ Don't mix tabs and spaces.
+ Separate logical steps or "paragraphs" with blank lines.
* Put closing curly braces `}` on their own line.
+ Exception: use `} else {`
* Use a consistent naming style:
+ `lowercase_with_underscores`
+ `camelCase`
* Use descriptive variable names. Short names are okay for frequently-used
variables where the context makes the meaning apparent.
* Use comments:
+ To create a big picture plan for how to write your code.
+ To explain tricky code.
+ To summarize the purpose of a "paragraph" of code.
+ To document how to use your functions. Also see roxygen2.
Without formatting:
```{r}
f=function(x,xx){
xx=match.arg(xx,c("celsius","fahrenheit"),several.ok=TRUE)
x1=xx=="fahrenheit"
x[x1]=(x[x1]-32)*5/9
x+273.15}
```
With formatting:
```{r}
to_kelvin = function(temp, unit) {
unit = match.arg(unit, c("celsius", "fahrenheit"), several.ok = TRUE)
is_f = unit == "fahrenheit"
temp[is_f] = (temp[is_f] - 32) * 5 / 9
temp + 273.15
}
```
Defensive Programming
---------------------
Test whether inputs satisfy your assumptions.
For instance, if your function should only work on scalars, check the
lengths of the arguments.
Raise an error with `stop()` if the assumptions aren't satisfied.
Useful functions for testing assumptions:
* `is.X()` functions for testing various properties. For instance,
`is.character()` to test whether an object is a character vector.
* `inherits()` for testing the class of an object.
* `length()`, `dim()` for testing dimensions.
The R Debugger
==============
Debugging code is the process of confirming, step-by-step, that what you belive
the code does is what the code actually does.
The key idea is to check each step of the code.
Two strategies:
* Work forward through the code from the beginning.
* Work backward from the source of an error.
R has built-in functions to help with debugging.
The `browser()` function pauses the running code and starts R's
debugging system.
For example:
```{r}
f = function(n) {
total = 0
for (i in 1:n) {
#browser()
total = total + i
}
total
}
f(10)
```
The key debugger commands are:
* `n` to run the next line
* `s` to "step into" a call
* `c` to continue running the code
* `Q` to quit the debugger
* `where` to print call stack
* `help` to print debugger help
Another example:
```{r}
g = function(x, y) (1 + x) * y
f = function(n) {
total = 0
for (i in 1:n) {
#browser()
total = total + g(i, i)
}
total
}
f(11)
```