2

I am preparing a package where users have to store functions and their associated arguments in a list for later use. This is a typical example of how it works:

Preparation of the list containing function names and arguments

parameters <- list(a1 = list(fun = dnorm,
                             args = list(mean = 150, sd = 100)),
                   a2 = list(fun = dpois,
                             args = list(lambda = 40)))

Here is an example of how this parameters object is used in other functions:

x <- 1:100
myfunction <- function(x, var, parameters)
{
    results <- list(var = var,
                    y = do.call(parameters[[var]]$fun, 
                                c(list(x), parameters[[var]]$args)),
                    parameters = parameters)
    class(results) <- "customclass"
    return(results)
}
myfunction(x, "a1", parameters)

Now suppose I want to print the names of the functions stored in parameters in the output of myfunction.

print.customclass <- function(X)
{
   cat("Function name:", X$parameters[[X$var]]$fun)
}

This custom print will not work:

> myfunction(x, "a1", parameters)
Error in cat(list(...), file, sep, fill, labels, append) : 
argument 2 (type 'closure') cannot be handled by 'cat' 

I have also tried other suggested solutions found elsewhere, such as:

print.customclass <- function(X)
{
   cat("Function name:", deparse(quote(X$parameters[[X$var]]$fun)))
}

> myfunction(x, "a1", parameters)
Function name: X$parameters[[X$var]]$fun

According to Harlan in this other question, the name of the function is lost when stored in another R object. He suggested to name the list of function with the function names, e.g.:

parameters <- list(a1 = list(dnorm = dnorm,
                             args  = list(mean = 150, sd = 100)),
                   a2 = list(dpois = dpois,
                             args  = list(lambda = 40)))

However, I wanted to keep the same names (fun) for every element of my list.

My question therefore is

Can anybody think of a way to keep the function name, when the function is stored in another object?

Thank you very much for your help!

Community
  • 1
  • 1
Boris Leroy
  • 460
  • 4
  • 14

2 Answers2

5

I'd suggest to define your parameters argument with the function name, instead of the function itself and retrieve the function in the body of myfunction through match.fun:

    parameters <- list(a1 = list(fun = "dnorm",
                         args = list(mean = 150, sd = 100)),
               a2 = list(fun = "dpois",
                         args = list(lambda = 40)))

    myfunction <- function(x, var, parameters)
    {
        results <- list(var = var,
                y = do.call(match.fun(parameters[[var]]$fun), 
                            c(list(x), parameters[[var]]$args)),
                parameters = parameters)
        class(results) <- "customclass"
        return(results)
    }
nicola
  • 24,005
  • 3
  • 35
  • 56
  • This is another nice answer, just as valid as @Roland 's answer. I have not yet decided which one I will implement, but both seem very valid to me. – Boris Leroy Sep 02 '14 at 11:40
4

You could use alists:

parameters <- list(a1 = alist(fun = dnorm,
                             args = list(mean = 150, sd = 100)),
                   a2 = alist(fun = dpois,
                             args = list(lambda = 40)))
#probably better to write a constructor method for parameters objects


x <- 1:100
myfunction <- function(x, var, parameters)
{ f <- function(x) do.call(fun, c(list(x), args))
  formals(f) <- c(formals(f), parameters[[var]]) 
  results <- list(var = var,
                  y = f(x),
                  parameters = parameters)
  class(results) <- "customclass"
  return(results)
}

print.customclass <- function(X)
{
  cat("Function name:", X$parameters[[X$var]]$fun)
}

myfunction(x, "a1", parameters)
#Function name: dnorm
Roland
  • 127,288
  • 10
  • 191
  • 288
  • That's a nice answer, I did not know about `alist`. You are right, I should write a constructor method for parameter objects. I am not yet sure how I can write it to make it very user-friendly. – Boris Leroy Sep 02 '14 at 11:38
  • @BorisLeroy I've changed the function a bit since `eval`ing like that seemed a bit clunky. – Roland Sep 02 '14 at 12:30