forked from Tazinho/Advanced-R-Solutions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path2-05-Control_flow.Rmd
83 lines (56 loc) · 3.55 KB
/
2-05-Control_flow.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
```{r, include=FALSE}
source("common.R")
```
# Control flow
## Choices
1. __[Q]{.Q}__: What type of vector does each of the following calls to `ifelse()` return?
```{r, eval = FALSE}
ifelse(TRUE, 1, "no")
ifelse(FALSE, 1, "no")
ifelse(NA, 1, "no")
```
Read the documentation and write down the rules in your own words.
__[A]{.solved}__: The arguments of `ifelse()` are `test`, `yes` and `no`. `ifelse()` is vectorised, so when `yes` or `no` are shorter than `test`, they will be recycled. (When they are longer than `test`, their additional elements will be ignored.)
The documentation says:
> A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no. The mode of the answer will be coerced from logical to accommodate first any values taken from yes and then any values taken from no.
This surprising because it uses the type of `test` ...
The function returns the entry for `yes` when `test` is `TRUE`, `no` when `test` is `FALSE` or `NA` when `test` is `NA`. Therefore, the expressions above return vectors of type `double`, `character` and `logical`.
2. __[Q]{.Q}__: Why does the following code work?
```{r}
x <- 1:10
if (length(x)) "not empty" else "empty"
x <- numeric()
if (length(x)) "not empty" else "empty"
```
__[A]{.solved}__: `if()` expects a logical condition, but also accepts a numeric vector where `0` is treated as `FALSE` and all other numbers are treated as `TRUE`. Numeric missing values (including `NaN`) lead to an error in the same way that a logical missing, `NA`, does.
## Loops
1. __[Q]{.Q}__: Why does this code succeed without errors or warnings?
```{r, results = FALSE}
x <- numeric()
out <- vector("list", length(x))
for (i in 1:length(x)) {
out[i] <- x[i] ^ 2
}
out
```
__[A]{.solved}__: The vector we are iterating over are of length 0. Our loop goes from `i = 1` to `i = 0` because `:` counts down, as well as up. As we use `[<-` and `[` for indexing, we need to be aware of their subsetting behaviour for out-of-bounds and zero indices.
During the first iteration `x[1]` will generate an `NA` (out-of-bounds indexing for atomics). The resulting `NA` (from squaring) will be assigned to the empty length-1 list `out[1]` (out-of-bounds indexing for lists).
In the next iteration, `x[0]` will return `numeric(0)` (zero indexing for atomics). Again squaring doesn't change the value and `numeric(0)` is assigned to `out[0]` (zero indexing for lists). Assigning a 0-length vector to a 0-length subset works, but doesn't change anything.
Overall the code works, because each step includes valid R operations. (Though it may have been very helpful to warn the user about the unusual inputs to this loop.)
2. __[Q]{.Q}__: What does the following code tell you about when the vector being iterated over is evaluated?
```{r}
xs <- c(1, 2, 3)
for (x in xs) {
xs <- c(xs, x * 2)
}
xs
```
__[A]{.solved}__: In this loop `x` takes on the values of the initial `xs` (`1`, `2` and `3`), indicating that it is evaluated just once in the beginning of the loop, not after each iteration.
3. __[Q]{.Q}__: What does the following code tell you about when the index is updated?
```{r}
for (i in 1:3) {
i <- i * 2
print(i)
}
```
__[A]{.solved}__: In a `for`-loop the index is updated in the beginning of each iteration. Therefore, reassigning the index symbol during one iteration doesn't affect the following iterations.