Computers: good at applying rigid rules over and over again. Humans: not so good at this. Iteration is at the heart of programming
Summary of methods for performing iteration in R:
for()
, while()
loopsapply()
family of functions**ply()
family of functionsControl flow:
if()
, else if()
, else
: standard conditionalsifelse()
: conditional function that vectorizes nicelyswitch()
: handy for deciding between several optionsBoolean operators:
&
work |
like +
or *
: they combine terms elementwise&&
and ||
give just a single boolean, lazilyIn summary, use &&
and ||
for conditionals, &
and |
for indexing or subsetting
set.seed(0)
x = runif(10, -1, 1)
x
## [1] 0.7933944 -0.4689827 -0.2557522 0.1457067 0.8164156 -0.5966361
## [7] 0.7967794 0.8893505 0.3215956 0.2582281
x[0 <= x & x <= 0.5] = 999 # Elementwise AND
x
## [1] 0.7933944 -0.4689827 -0.2557522 999.0000000 0.8164156
## [6] -0.5966361 0.7967794 0.8893505 999.0000000 999.0000000
(0 > 0) && all(matrix(0,2,2) == matrix(0,3,3)) # Lazy AND
## [1] FALSE
(0 > 0) && (ThisVariableIsNotDefined == 0) # Lazy AND
## [1] FALSE
In the last two lines, R never evaluates the expression on the right (each of these would throw an error on its own!)
for()
loopA for()
loop increments a counter variable along a vector. It repeatedly runs a code block, called the body of the loop, with the counter set at its current value, until it runs through the vector
n = 10
log.vec = vector(length=n, mode="numeric")
for (i in 1:n) {
log.vec[i] = log(i)
}
log.vec
## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
## [8] 2.0794415 2.1972246 2.3025851
Here i
is the counter and the vector we are iterating over is 1:n
. The body is the code in between the braces
We can break out of a for()
loop early (before the counter has been iterated over the whole vector), using break
n = 10
log.vec = vector(length=n, mode="numeric")
for (i in 1:n) {
if (log(i) > 2) {
cat("I'm outta here. I don't like numbers bigger than 2\n")
break
}
log.vec[i] = log(i)
}
## I'm outta here. I don't like numbers bigger than 2
log.vec
## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
## [8] 0.0000000 0.0000000 0.0000000
for()
loopsMany different variations on standard for()
are possible. Two common ones to be aware of:
for()
loop can contain another for()
loop (or several others)for (str in c("Prof", "Ryan", "Tibs")) {
cat(paste(str, "declined to comment\n"))
}
## Prof declined to comment
## Ryan declined to comment
## Tibs declined to comment
for (i in 1:4) {
for (j in 1:i^2) {
cat(paste(j,""))
}
cat("\n")
}
## 1
## 1 2 3 4
## 1 2 3 4 5 6 7 8 9
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
while()
loop: conditional iterationA while()
loop repeatedly runs a code block, called the body, until some condition is no longer TRUE
i = 1
log.vec = c()
while (log(i) <= 2) {
log.vec = c(log.vec, log(i))
i = i+1
}
log.vec
## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
for()
versus while()
for()
is better when the number of times to repeat (values to iterate over) is clear in advance
while()
is better when you can recognize when to stop once you’re there, even if you can’t guess it to begin with
while()
is more general, in that every for()
could be replaced with a while()
(but not vice versa)
while(TRUE)
or repeat
: unconditional iterationwhile(TRUE)
and repeat
: both have the same function, just repeat the body indefinitely, until something causes the flow to break
Try running the code below in your console
repeat {
ans = readline("Who is the best Professor of Statistics at CMU? ")
if (ans == "Tibs" || ans == "Tibshirani" || ans == "Ryan") {
cat("Yes! You get an 'A'.")
break
}
else {
cat("Wrong answer!\n")
}
}
Warning: some people have a tendency to overuse for()
and while()
loops in R. They aren’t always needed. Useful alternatives:
apply()
family of functions: apply a given function over dimensions of an object. E.g., elements of a vector, elements of a list, rows/columns of a matrix. Often simpler, not often faster**ply()
family of functions: like apply()
family, but much more transparent about what goes in and what comes out