Home > other >  R Why do I have to assign a formal argument variable to itself in order for this function to work?
R Why do I have to assign a formal argument variable to itself in order for this function to work?

Time:10-30

I have developed the following two functions:

save_sysdata <- function(...) {
  data <- eval(substitute(alist(...)))
  data <- purrr::map_chr(data, add_dot)
  save(list = data, file = "sysdata.rda", compress = "bzip2", version = 2)
}

add_dot <- function(object) {
  object <- object # Why is this required?
  name <- paste0(".", deparse(substitute(object)))
  # parent.frame(3) because evaluating in global (or caller function); 2 because assigning in save_sysdata.
  assign(name, eval(object, envir = parent.frame(3)), envir = parent.frame(2))
  return(name)
}

The purpose of this set of functions is to provide an object (x) and save it as a sysdata.rda file but as a hidden object. This requires adding a . to the object symbol (.x).

The set of functions as I have it works and accomplishes what I want. However, it requires a bit of code that I don't understand why it works or what it's doing. I'm not even sure how I came up with this particular line as a solution.

If I remove the line object <- object from the add_dot function, the whole thing fails to work. It actually just generates an empty sysdata.rda file.

Can anyone explain why this line is necessary and what it is doing?

And if you have a more efficient way of accomplishing this, please let me know. It was a fun exercise to figure this out myself but I'm sure there is a better way.

For a reprex, simply copy the above functions and run:

x <- "test"
save_sysdata(x)

Then load the sysdata.rda file into your global environment and type .x. You should return [1] "test".

CodePudding user response:

Here's an alternative version

save_sysdata <- function(...)  {
  pnames <- sapply(match.call(expand.dots=FALSE)$..., deparse)
  snames <- paste0(".", pnames)
  senv <- setNames(list(...), snames)
  save(list = snames, envir=list2env(senv), file = "sysdata.rda", compress = "bzip2", version = 2)
}

We dump the values into a named list and granbing the names of the parameter with match.call(). We add dots to the names and then turn that list into an environment that we can use with save.

The reason your version required object <- object is that function parameters are lazily evaluated. Since you never actually use the value of that object in your function without the assignment, it remains a promise and is never added tot he function environment. Sometimes you'll see force(object) instead which does the same thing.

  •  Tags:  
  • r
  • Related