HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

2D lattice random walk plots in functional style

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomplotslatticestylewalkfunctional

Problem

To practice writing code in the functional programming style, I wrote a program to plot two-dimensional lattice random walks. I'd appreciate any feedback about how to improve its "functionaliness".

possibleSteps <- list(c(-1,0), c(0,-1), c(1,0), c(0,1))

step <- function(x) {
        return(unlist(sample(possibleSteps, 1)))
}

takeRandomWalk <- function(nSteps) {
        coordPairs <- Reduce(`+`, lapply(1:nSteps, step), accumulate = T)
        x <- sapply(coordPairs, `[`, 1)
        y <- sapply(coordPairs, `[`, 2)
        return(list(x, y))
}

plotRandomWalk <- function(nSteps, margins) {
        walkObj <- takeRandomWalk(nSteps)
        plot(seq(-margins,margins), seq(-margins,margins),
             type = 'n', xlab = "", ylab = "")
        lines(walkObj[[1]], walkObj[[2]])
}


Call plotRandomWalk(10000, 80) for an example.

EDIT

I have now compiled a far schnazier version as a shiny app:

Check it out!

Thanks for the help!

Solution

Looks good. The code is functional with the exception of using
return, which is not necessary as the last value will be returned
anyway, so I'd simplify to the following and also in the interested of
efficiency only compute the coords once and without seq, as : is
shorter:

step <- function(x) {
        unlist(sample(possibleSteps, 1))
}

takeRandomWalk <- function(nSteps) {
        coordPairs <- Reduce(`+`, lapply(1:nSteps, step), accumulate = T)
        x <- sapply(coordPairs, `[`, 1)
        y <- sapply(coordPairs, `[`, 2)
        list(x, y)
}

plotRandomWalk <- function(nSteps, margins) {
        walkObj <- takeRandomWalk(nSteps)
        coords <- -margins:margins
        plot(coords, coords, type = 'n', xlab = "", ylab = "")
        lines(walkObj[[1]], walkObj[[2]])
}


Reduce with accumulate is pretty cool, I think this is the first
time I've seen an accumulate parameter on a reduce-like function
anywhere.

Now I think you know that, but usually R code like this would be written
with less lists and more data frames or matrixes. That doesn't mean the
code isn't functional: As long as it's using functions, not mutating
things and using functional abstractions instead of imperative
constructs it's fine.

So the following is probably more idiomatic and at the same time uses
more efficient representations for the data:

possibleSteps <- matrix(c(-1, 0, 0, -1, 1, 0, 0, 1), ncol = 2, byrow = TRUE,
                        dimnames = list(NULL, c("X", "Y")))


Gives a matrix with named columns like so, which will be the representation
throughout the rest of the code (which is good for consistency):

> possibleSteps
      X  Y
[1,] -1  0
[2,]  0 -1
[3,]  1  0
[4,]  0  1


The random walk will still be created by sample, except it's sampling
indexes. cbind will create a new matrix from the two accumulated sums
(cumsum):

takeRandomWalk <- function(nSteps) {
        indexes <- sample(1:dim(possibleSteps)[1], nSteps, TRUE)
        walk <- possibleSteps[indexes,]
        cbind(X = cumsum(walk[,1]), Y = cumsum(walk[,2]))
}


And finally plotRandomWalk is a bit simpler as we can just give
lines the constructed coordinates matrix:

plotRandomWalk <- function(nSteps, margins) {
        coords <- -margins:margins
        plot(coords, coords, type = 'n', xlab = "", ylab = "")
        lines(takeRandomWalk(nSteps))
}

Code Snippets

step <- function(x) {
        unlist(sample(possibleSteps, 1))
}

takeRandomWalk <- function(nSteps) {
        coordPairs <- Reduce(`+`, lapply(1:nSteps, step), accumulate = T)
        x <- sapply(coordPairs, `[`, 1)
        y <- sapply(coordPairs, `[`, 2)
        list(x, y)
}

plotRandomWalk <- function(nSteps, margins) {
        walkObj <- takeRandomWalk(nSteps)
        coords <- -margins:margins
        plot(coords, coords, type = 'n', xlab = "", ylab = "")
        lines(walkObj[[1]], walkObj[[2]])
}
possibleSteps <- matrix(c(-1, 0, 0, -1, 1, 0, 0, 1), ncol = 2, byrow = TRUE,
                        dimnames = list(NULL, c("X", "Y")))
> possibleSteps
      X  Y
[1,] -1  0
[2,]  0 -1
[3,]  1  0
[4,]  0  1
takeRandomWalk <- function(nSteps) {
        indexes <- sample(1:dim(possibleSteps)[1], nSteps, TRUE)
        walk <- possibleSteps[indexes,]
        cbind(X = cumsum(walk[,1]), Y = cumsum(walk[,2]))
}
plotRandomWalk <- function(nSteps, margins) {
        coords <- -margins:margins
        plot(coords, coords, type = 'n', xlab = "", ylab = "")
        lines(takeRandomWalk(nSteps))
}

Context

StackExchange Code Review Q#83460, answer score: 4

Revisions (0)

No revisions yet.