Sonntag, 1. Mai 2016

R: Replacement functions

If you use R, you've been using so called replacement functions such as '<-' to assign a value to a variable or 'colnames' to define names of columns in a data frame.
Remember, in R everything operation is a function call (therefore also the assignment operations).
In the following, the behaviour of the replacement functions are illustrated (and compared to a -failing- ordinary approach) and code is shown to list all replacement functions in the base R package.

Replacement functions act as if they modify their arguments in place such as in

colnames(d) <- c("Input", "Output")

They have the identifier '<-' at the end of their name and return a modified copy of the argument object (non-primitive replacement functions) or the same object (primitive replacement functions).

At the R prompt, the following will not work:

> `second` <- function(x, value) {
+   x[2] <- value
+   x
+ }
> x <- 1:10
> x
 [1]  1  2  3  4  5  6  7  8  9 10
> second(x) <- 9
Fehler in second(x) <- 9 : konnte Funktion "second<-" nicht finden


As one can see, behind the scenes, R is looking for a function called 'second<-'. So lets do the same thing but using this name:

> `second<-` <- function(x, value) {
+   x[2] <- value
+   x
+ }


Now, the assignment at the second position of the vector works:

> second(x) <- 9
> x
 [1]  1  9  3  4  5  6  7  8  9 10


The following code allows to list all replacement functions in are and check if they are primitive:



# Get all objects from the base package, then
# functions from among those objects.
objs <- mget(ls("package:base"), inherits=TRUE)
funs <- Filter(is.function, objs)
 
# Replacement functions have '<-' at the end of their name.
get_rep_fun <- function(fun_i) {
  if(substr(fun_i, nchar(fun_i) - 1, nchar(fun_i)) == "<-") {
    fun_i
  }
}
 
# The list returned by 'lapply' contains many 'NULL'
# entries. Only non-'NULL' function name strings remain
# after 'unlist'.
rep_funs <- lapply(names(funs), get_rep_fun)
rep_funs <- unlist(rep_funs)
 
# Identify primitive replacement functions.
get_prim_rep_fun <- function(rep_fun) {
  if(is.primitive(rep_fun)) {
    rep_fun
  }
}
 
# Get the actual function object by 'sapply'-ing 'get' to
# each string identifier from the 'rep_funs' vector.
prim_rep_funs <- lapply(sapply(rep_funs, get), is.primitive)
 
# Prepare data frame for convenience.
k <- data.frame(fun_name = rep_funs, prim=unlist(prim_rep_funs))
 
# Rows are named after functions -> reduce redundancy.
rownames(k) <- NULL