-1

Consider the function

f <- function(x, X) mean(c(x,X))

How can I automatically (by manipulation of f()) change the signature of f() such that it can be used with lapply(), i.e., without returning the following obvious error?

lapply(X=list(1), FUN=f, X=1)
Error in lapply(X = list(1), FUN = f, X = 1) : 
  formal argument "X" matched by multiple actual arguments

The approach I used so far is to remove all arguments from f(), assign them into an environment, and evaluatef() in that environment.

integrateArgs <- function (f, args) 
{
    form <- formals(f)
    if (!is.null(form)) 
        for (i in seq_along(form)) assign(names(form)[i], form[[i]])
    if (!is.null(args)) 
        for (i in seq_along(args)) assign(names(args)[i], args[[i]])
    ff <- function() {
    }
    parent.env(environment(ff)) <- parent.env(environment(f))
    body(ff) <- body(f)
    if (any(names(form) == "...")) 
        formals(ff) <- form[names(form) == "..."]
    ff
}
fnew <- integrateArgs(f, list(x=1, X=4))
lapply(list(fnew), function(x) x())
[[1]]
[1] 2.5

However, that approach leads to the following error if f() is a function from another R package that calls compiled code.

fnew2 <- integrateArgs(dnorm, list(x=1, mean=4))
lapply(list(fnew2), function(x) x())
Error in x() (from #1) : object 'C_dnorm' not found

Are there better solutions?

Nairolf
  • 2,418
  • 20
  • 34
  • Down voting the question without corresponding comment is not fair and not helpful ;-) – Nairolf Oct 12 '18 at 03:42
  • 1
    You are writing to call `lapply` with two parameters named `X=`. (Did you want one of those to go to little `x=`?) That just doesn't seem possible given the language definition. If you are not willing to change the signature of `f`, I don't think there's anything you can do. – MrFlick Oct 12 '18 at 03:55
  • This is obvious, you are right. However this is not what wanted to ask. I updated the question to make it more precise. – Nairolf Oct 12 '18 at 04:07
  • 1
    You will never be able to type the line `lapply(X=list(1), FUN=f, X=1)` and have it work because there are two `X=`. So if that's a requirement, we're stuck. The reason to change `f` would be so that it doesn't have a parameter named `X=`. Maybe a bit more clarification and a proper reproducible example would help for testing? – MrFlick Oct 12 '18 at 04:15
  • You are right and the question needed to be reformulated. I think it is more clear now. – Nairolf Oct 12 '18 at 04:47
  • Using `lapply` is motivated by the parallel processing options, i.e., when `lapply` is replaced by `parLapply`. – Nairolf Oct 12 '18 at 05:10
  • 1
    It looks like you are trying to create a partial function. That's basically what `purrr`'s `partial()` function will do. The only catch is that it doesn't like a list of arguments, it wants them as parameters. But you can use `do.call` to take care of that. `integrateArgs <- function(f, args) {do.call(purrr::partial, c(list(f), args))}` seems to work in this case. – MrFlick Oct 12 '18 at 15:29
  • Great! This answers the question. - And with a few lines of code one can avoid the dependency on the R package `purrr` leaving the dependency on `rlang`. Removing the dependency on `rlang` requires the integration of compiled code. That is probably not worth it. – Nairolf Oct 12 '18 at 16:23

1 Answers1

0

As suggested in a comment by MrFlick, one solution is

library(purrr)
integrateArgs <- function(f, args){
    do.call(partial, c(list(f), args))
}
fnew2 <- integrateArgs(dnorm, list(x=1, mean=4))
lapply(list(fnew2), function(x) x())
[[1]]
[1] 0.004431848

The following similar approach does not require the package purrr:

integrateArgs <- function(f, args){
    do.call(function(f, ...) {
        eval(call("function", NULL,
                  substitute(f(...))), envir = environment(f))}, 
        c(f = list(f), args))
}
fnew2 <- integrateArgs(dnorm, list(x=1, mean=4))
lapply(list(fnew2), function(x) x())
[[1]]
[1] 0.004431848

A similar approach is now used in optimParallel version 0.7-4 to execute functions in parallel using parallel::parLapply(): https://cran.r-project.org/package=optimParallel

Nairolf
  • 2,418
  • 20
  • 34