I learned a new trick in R — replicate
to repeat random number
generation. I also learned that it’s not necessary to understand R to use it.
This was prompted by discussion of queueing theory, with code in R (“queue“). For a simpler case, suppose we’re investigating random distributions and seeing how many sixes we get when we roll a die ten times.
Function parameters in R are passed by value. This means that, while
sum(sample(1:6, 10, replace='T') == 6)
will give a random number,
rep(sum(sample(1:6, 10, replace='T') == 6), 10)
will take a sequence
of ten rolls and then repeat
it ten times — not what we want:
[1] 2 2 2 2 2 2 2 2 2 2
I’d been using this technique to make each repetition pick a new number:
> sapply(1:10, function(x){sum(sample(seq(1,6), 10, replace='T') == 6)}) [1] 1 0 1 0 1 1 0 1 2 0
Here, for each item in the initial list, the function is evaluated. R is also lazy — the expressions used as parameters aren’t evaluated until they’re used. To demonstrate:
> sideEffect <- function(s) {print(paste("Evaluated for", s)); s;} > f <- function(x, y) {x} > dummy <- f(sideEffect("First"), sideEffect("Second")) [1] "Evaluated for First" > f <- function(x, y) {c(x,y)} > dummy <- f(sideEffect("First"), sideEffect("Second")) [1] "Evaluated for First" [1] "Evaluated for Second"
Here, the first function call only uses its first argument, so the second
sideEffect
is never called.
Together, that doesn’t help to write a function that evaluates its argument repeatedly. However, R is also flexible enough to allow redefining the arguments before evaluating them:
> replicate function (n, expr, simplify = "array") sapply(integer(n), eval.parent(substitute(function(...) expr)), simplify = simplify) <environment: namespace:base>
Now, the expr
argument is evaluated once each time it’s used.
So, replicate(10, sum(sample(seq(1,6), 10, replace='T') == 6))
gives me exactly what I want.