R Language The Other Looping Constructs: while and repeat


Example

R provides two additional looping constructs, while and repeat, which are typically used in situations where the number of iterations required is indeterminate.


The while loop

The 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.


The repeat loop

The 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 

More on 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
}