``` {r echo=FALSE, results="hide"} knitr::opts_chunk$set( error=FALSE, fig.width=7, fig.height=5) set.seed(1)
The bytes-based ring buffer in the main vignette is a better data structure to implement the simulation than the environment buffer is because the expected elements in each entry of the buffer are the same. But with a bit of S3 syntactic sugar we can do a bit better. This vignette is an attempt at creating "ring" versions of a vector and matrix data type. NOTE: this vignette used to be implemented in the package itself, but I pulled it out of the package because I felt that the implementation wasn't quite right, and that it may not be ideal to have objects that appear to have normal-R semantics operate by side effect. However, this may give some ideas for how to use ring buffers in practice. ## Ring vector The actual code for the buffer here is available in the package via `system.file("examples/ring_vector.R", package = "ring")` (the path depends on your R and package installations). ``` {r echo = FALSE, results = "asis"} local({ path <- system.file("examples/ring_vector.R", package = "ring") source(path, local = FALSE) writeLines(c("```r", readLines(path), "```")) })
Then create an integer ring vector of length 5:
v <- ring_vector(5, "integer", FALSE)
Convert back out to be an R vector (involves a copy)
v[]
To add things to the vector, use the push generic:
push(v, 1L) v[]
This can push multiple items on at once:
push(v, 2:4) v[] length(v)
Random read access works:
v[3] v[[1]]
Resetting the buffer zeros this all:
v$buf$reset() length(v)
Returning to the simulation example from the main vignette:
buf <- ring_vector(5, "integer", FALSE) h <- integer(20) x <- 0L push(buf, x) h[1L] <- x step <- function(x) { if (runif(1) < 0.5) x - 1L else x + 1L } set.seed(1) for (i in seq_len(length(h) - 1L)) { x <- step(x) push(buf, x) h[i + 1L] <- x }
The whole history:
h
The last 5 steps:
buf[]
Now, rewriting again, this time with the step function taking the
buffer itself. This simplifies the implementation, with most of
the details being handled by the S3 methods for length, push
and [.
step <- function(x) { if (length(x) > 1) { p <- mean(diff(x[])) / 2 + 0.5 } else { p <- 0.5 } if (runif(1) < p) x[length(x)] - 1L else x[length(x)] + 1L } buf <- ring_vector(5, "integer", FALSE) h <- integer(100) x <- 0L push(buf, x) h[1L] <- x set.seed(1) for (i in seq_len(length(h) - 1L)) { x <- step(buf) push(buf, x) h[i + 1L] <- x } par(mar=c(4, 4, .5, .5)) plot(h, type="l", xlab="step", ylab="y", las=1)
ring_matrixThe ring_matrix data structure generalises the ring_vector; it
is a buffer that looks to R like a matrix that grows by adding rows
at the bottom and shrinks by consuming rows at the top.
{r echo = FALSE, results = "asis"}
local({
path <- system.file("examples/ring_matrix.R", package = "ring")
source(path, local = FALSE)
dat <- readLines(path)
writeLines(c("r", dat[!grepl("^###", dat)], "```"))
})
This is even more contrived than above, but consider simultaneously simulating the movement of `n` random particles with the same reflecting random walk as above. First create a 10 x 5 ring matrix: ``` {r } n <- 10 m <- ring_matrix(5, n, "integer", FALSE)
The current state of the matrix is:
m[]
We can set the initial state as:
push(m, matrix(0L, 1, n)) m[] step <- function(m) { if (nrow(m) > 1) { p <- colMeans(diff(m[])) / 2 + 0.5 } else { p <- rep(0.5, ncol(m)) } m[nrow(m), ] + as.integer(ifelse(runif(length(p)) < p, -1, 1L)) } m <- ring_matrix(5, n, "integer", FALSE) x <- rep(0L, n) push(m, x) h <- matrix(NA, 200, n) h[1, ] <- x set.seed(1) for (i in seq_len(nrow(h) - 1L)) { x <- step(m) push(m, x) h[i + 1L, ] <- x } par(mar=c(4, 4, .5, .5)) matplot(h, type="l", lty=1, las=1)
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.