R provides two additional looping constructs, while
and repeat
, which are typically used in situations where the number of iterations required is indeterminate.
while
loopThe general form of a while
loop is as follows,
while (condition) {
## do something
## in loop body
}
where condition
is evaluated prior to entering the loop body. If condition
evaluates to TRUE
, the code inside of the loop body is executed, and this process repeats until condition
evaluates to FALSE
(or a break
statement is reached; see below). Unlike the for
loop, if a while
loop uses a variable to perform incremental iterations, the variable must be declared and initialized ahead of time, and must be updated within the loop body. For example, the following loops accomplish the same task:
for (i in 0:4) {
cat(i, "\n")
}
# 0
# 1
# 2
# 3
# 4
i <- 0
while (i < 5) {
cat(i, "\n")
i <- i + 1
}
# 0
# 1
# 2
# 3
# 4
In the while
loop above, the line i <- i + 1
is necessary to prevent an infinite loop.
Additionally, it is possible to terminate a while
loop with a call to break
from inside the loop body:
iter <- 0
while (TRUE) {
if (runif(1) < 0.25) {
break
} else {
iter <- iter + 1
}
}
iter
#[1] 4
In this example, condition
is always TRUE
, so the only way to terminate the loop is with a call to break
inside the body. Note that the final value of iter
will depend on the state of your PRNG when this example is run, and should produce different results (essentially) each time the code is executed.
repeat
loopThe repeat
construct is essentially the same as while (TRUE) { ## something }
, and has the following form:
repeat ({
## do something
## in loop body
})
The extra {}
are not required, but the ()
are. Rewriting the previous example using repeat
,
iter <- 0
repeat ({
if (runif(1) < 0.25) {
break
} else {
iter <- iter + 1
}
})
iter
#[1] 2
break
It's important to note that break
will only terminate the immediately enclosing loop. That is, the following is an infinite loop:
while (TRUE) {
while (TRUE) {
cat("inner loop\n")
break
}
cat("outer loop\n")
}
With a little creativity, however, it is possible to break entirely from within a nested loop. As an example, consider the following expression, which, in its current state, will loop infinitely:
while (TRUE) {
cat("outer loop body\n")
while (TRUE) {
cat("inner loop body\n")
x <- runif(1)
if (x < .3) {
break
} else {
cat(sprintf("x is %.5f\n", x))
}
}
}
One possibility is to recognize that, unlike break
, the return
expression does have the ability to return control across multiple levels of enclosing loops. However, since return
is only valid when used within a function, we cannot simply replace break
with return()
above, but also need to wrap the entire expression as an anonymous function:
(function() {
while (TRUE) {
cat("outer loop body\n")
while (TRUE) {
cat("inner loop body\n")
x <- runif(1)
if (x < .3) {
return()
} else {
cat(sprintf("x is %.5f\n", x))
}
}
}
})()
Alternatively, we can create a dummy variable (exit
) prior to the expression, and activate it via <<-
from the inner loop when we are ready to terminate:
exit <- FALSE
while (TRUE) {
cat("outer loop body\n")
while (TRUE) {
cat("inner loop body\n")
x <- runif(1)
if (x < .3) {
exit <<- TRUE
break
} else {
cat(sprintf("x is %.5f\n", x))
}
}
if (exit) break
}