Dynamically creating a function within an R packag

2019-05-12 04:08发布

问题:

I have a dynamically generated function (for reasons explained in the spoiler block at the bottom if you're curious) that I want to include in a package. I want to do so with minimal performance penalty and without using unsupported functionality. The way I thought of doing this so far is putting the code that generates the function into .onLoad() and either storing the function in options() or as a hidden object in .GlobalEnv. Obviously storing the function in .GlobalEnv is risky because it can be unintentionally modified or deleted. It can happen with options() too, less likely by accident.

Is storing generated functions in options() the best way to go?

I have a multivariate function, call it Fn with long, ugly derivatives. I would like to create a another function that returns a gradient of that function (i.e. a vector of first derivatives in each variable, evaluated at a given X) and a hessian (i.e. matrix of second derivatives in each combination of variables evaluated at a given X). To make the code maintainable, I only hand-code the original Fn as an unevaluated expression then let the D(), eval(), and `body<-`() do the rest of the work. I end up with a dynamically generated function object that returns the results I want.


Adam Hyland's comment has the simplest answer to this specific question. If you post it as an answer, it will be accepted. However, Richie Cotton's answer is very useful in the general case, so thank you as well.

回答1:

You could try using assignInNamespace inside .onLoad (not tested) but like Adam H said in his comment, it sounds like you are doing things the hard way.

If I've understood the question correctly, you have an expression of derivatives, and you want to evaluate that expression at a given point, and calculate the gradient at that point, and calculate the hessian at that point. Just create a function that accepts an expression and a numeric vector of coordinates to evaluate at, and have it spit out all the things you want. Something like this:

#' Evaluate an expression, its derivative and its hessian
#' 
#' Evaluates an expression, its derivative and its hessian at a given point.
#' @param expr An expression of derivatives
#' @param x A named numeric vector of coords to evaluate \code{expr} at
#' @param name String giving the name of the variable to differentiate by
#' @return A list with the following values
#' \itemize{
#'   \item{value}{The value of \code{expr} evaluated at \code{x}.}
#'   \item{gradient}{The value of the derivative of \code{expr} evaluated at \code{x}.}
#'   \item{hessian}{The value of the hessian of \code{expr} evaluated at \code{x}.}
#' }
#' @examples
#' expr <- expression(sin(cos(x + y^2)))
#' x <- c(x = pi / 2, y = pi / 3)
#' eval_expr_and_calc_grad_and_hessian(expr, x, "x")
eval_expr_and_calc_grad_and_hessian <- function(expr, x, name = names(x)[1])
{
  x <- as.list(x)
  d_by_dname <- D(expr, name)
  d2_by_dname2 <- D(d_by_dname, name)
  list(
    value    = eval(expr, x),
    gradient = eval(d_by_dname, x),
    hessian  = eval(d2_by_dname2, x)
  )
}


标签: r package