Reputation: 1915
Following on from How to create an R function programmatically?
I want to build R functions from their components like this:
testfn <- function(..., expression){
args <- substitute(alist(...))
body <- substitute(expression)
eval(call("function", as.pairlist(eval(args)), body), parent.frame())
}
This works fine if there are default values:
testfn(x = 4, y = 5, expression = {
y <- y + 2
x + y
})
=>
function (x = 4, y = 5)
{
y <- y + 2
x + y
}
But it will not work if there is no default argument for one or more of the args:
testfn(x = 4, y, expression = {
y <- y + 2
x + y
})
=>
Error in eval(expr, envir, enclos) :
invalid formal argument list for "function"
I can put =
in the arg list to get this to work:
testfn(x = 4, y = , expression = {
y <- y + 2
x + y
})
=>
function (x = 4, y)
{
y <- y + 2
x + y
}
But I don't want to enforce this in the function arguments. I have tried editing the alist in the function, but when I do this, I get another invalid formal argument list error:
testfn2 <- function(..., expression){
args <- substitute(alist(...))
body <- substitute(expression)
for (arg in 2:length(args)){
if(names(myargs)[arg] == ""){
args[[arg]] <- as.name(paste(as.character(args)[arg], "="))
}
}
eval(call("function", as.pairlist(eval(args)), body), parent.frame())
}
testfn2(x = 4, y, expression = {
y <- y + 2
x + y
})
=>
Error in eval(expr, envir, enclos) :
invalid formal argument list for "function"
How can I change this so I can call testfn() with missing argument defaults? I thought of constructing a new alist from a character string using parse
but this will mean I cannot have e.g. functions as arguments.
Upvotes: 5
Views: 2202
Reputation: 91
If I understand what you want to do, passing a zero-length symbol as the default value for y will do the trick:
> body <- expression(x + y)
>
> args <- list()
> args[["y"]] <- alist(y=)$y #zero-length symbol
> args[["x"]] <- 4
> eval(call("function", as.pairlist(args), body[[1]]))
function (y, x = 4)
x + y
There's no other way I've found (besides the above trick with alist) to generate one of these:
> as.symbol("")
Error in as.symbol("") : attempt to use zero-length variable name
But it's the value you have to provide in a formals list to get a mandatory argument.
Upvotes: 0
Reputation: 9
A little late for you, but may help others with the same issue. The "invalid formal argument list for "function"" error is a bug in RStudio. It happens when for some reason a breakpoint is set at the end bracket (}) of a function.
This happened to me when I was editing the R script while running it, and then the R studio crashed. the breakpoints were kept on the original lines, but the lines had moved...
All you need to do is remove the breakpoint, and you're good.
Upvotes: -1
Reputation: 49033
Here is one not very elegant way to do it :
testfn <- function(..., expression){
args <- eval(substitute(alist(...)))
body <- substitute(expression)
## Fix for missing default values
tmp <- names(args)
if (is.null(tmp)) tmp <- rep("", length(args))
names(args)[tmp==""] <- args[tmp==""]
args[tmp==""] <- list(NULL)
eval(call("function", as.pairlist(args), body), parent.frame())
}
Upvotes: 2