This question already has answers here:
Split up `...` arguments and distribute to multiple functions
(4 answers)
Closed 8 years ago.
I want to be able to take the ellipsis ... and use it in multiple child functions inside of a parent function. Doing so throws an error which is sensible as I am passing z to fun1 which has no z argument.
tester <- function(x, ...) {
list(a=x, b=fun1(...), c=fun2(...))
}
fun1 <- function(y) y * 6
fun2 <- function(z) z + 1
tester(z=4, y=5, x=6)
## > tester(z=4, y=5, x=6)
## Error in fun1(...) : unused argument (z = 4)
What is the most generalizable way to use arguments from an ellipsis in multiple child functions. Pretend the problem gets worse and we have 10000 child functions each getting different arguments from .... The desired output would be:
$a
[1] 6
$b
[1] 30
$c
[1] 5
I suspect it may be useful to capture the formals of each child function and match against the named arguments in ... but that seems less generalizable (but that may be as good as it gets).
If you can't change the input functions, you could try this:
tester <- function(x, ...) {
args <- as.list(match.call())[-1]
args1 <- head(names(as.list(args(fun1))), -1)
args2 <- head(names(as.list(args(fun2))), -1)
list(a=x, b=do.call(fun1, args[names(args) %in% args1]),
c=do.call(fun2, args[names(args) %in% args2]))
}
fun1 <- function(y) y * 6
fun2 <- function(z) z + 1
tester(z=4, y=5, x=6)
#$a
#[1] 6
#
#$b
#[1] 30
#
#$c
#[1] 5
It's awfully complicated and I wouldn't be surprised if you encounter dragons.
Here's #nicola's answer:
tester <- function(x, ...) {
list(a=x, b=fun1(...), c=fun2(...), d=path.expand2(...))
}
fun1 <- function(y, ...) y * 6
fun2 <- function(z, ...) z + 1
path.expand2 <- function(path, ...) path.expand(path)
tester(z=4, y=5, x=6, path="~/thepath")
## $a
## [1] 6
##
## $b
## [1] 30
##
## $c
## [1] 5
##
## $d
## [1] "C:/Users/trinker/thepath"
Related
f1<-function(x,y){
f2<-function(a,b){
print("f2")
return(a+b)}
f2(x,y)
print("f1")
return(x-y)}
f1(8,5)
I was trying above code to figure out the steps of operating function within function, so instead of writing two separate functions, I write the above code. But I can't get the output for a+b (which is 13)
[1] "f2"
[1] "f1"
[1] 3
#[1] 13, this output is missing.
How should the code be corrected? Thank you.
*additional question: when I only write x-y instead of return(x-y) at the last line of the function f1, I got the same output. Is simply write x-y a bad practice or accpetable?
-------------------------Update:
I just find out a way to get all the four outputs by changing the 4th line from return(a+b) to print(a+b)
or to make it more simple, only use the x,yarguments:
f1<-function(x,y) {
f2<-function() {
print("f2")
print(x+y)
}
f2()
print("f1")
x-y
}
while I still don't understand why using return(x+y) or simply x+y at the 4th line could not get the output of 13?
When an expression is on a line by itself it will automatically print if you do it at the R console but that does not happen if it is within a function or within an expression. Use cat or print for displaying.
To return two objects return a list containing both of them as shown at below.
The value of the last line that is run in a function is returned so you rarely need return.
f1a <- function(x, y) {
f2 <- function(a, b) {
print("f2")
a + b
}
print("f1")
list(x - y, f2(x, y))
}
result <- f1a(8, 5)
## [1] "f1"
## [1] "f2"
result[[1]]
## [1] 3
result[[2]]
## [1] 13
result
## [[1]]
## [1] 3
##
## [[2]]
## [1] 13
Other things we could do would be to replace the list(...) line in the code above with one of the following. (The c versions would only be used if we knew that the arguments were always scalars.)
list(f1 = x - y, f2 = f2(x, y)) # named list
c(x - y, f2(x, y)) # 2 element numeric vector
c(f1 = x - y, f2 = f2(x, y)) # 2 element named numeric vector
cbind(f1 = x - y, f2 = f2(x, y)) # matrix w column names
data.frame(f1 = x - y, f2 = f2(x, y)) # data.frame
Part of a custom function I am trying to create allows the user to provide a function as a parameter. For example
#Custom function
result <- function(.func){
do.call(.func, list(x,y))
}
#Data
x <- 1:2
y <- 0:1
#Call function
result(.func = function(x,y){ sum(x, y) })
However, the code above assumes that the user is providing a function with arguments x and y. Is there a way to use do.call (or something similar) so that the user can provide a function with different arguments? I think that the correct solution might be along the lines of:
#Custom function
result <- function(.func){
do.call(.func, formals(.func))
}
#Data
m <- 1:3
n <- 0:2
x <- 1:2
y <- 0:1
z <- c(4,6)
#Call function
result(.func = function(m,n){ sum(m, n) })
result(.func = function(x,y,z){ sum(x,y,z) })
But this is not it.
1) Use formals/names/mget to get the values in a list. An optional argument, envir, will allow the user to specify the environment that the variables are located in so it knows where to look. The default if not specified is the parent frame, i.e. the caller.
result1 <- function(.func, envir = parent.frame()) {
do.call(.func, mget(names(formals(.func)), envir))
}
m <- 1:3
n <- 0:2
x <- 1:2
y <- 0:1
z <- c(4,6)
result1(.func = function(m,n) sum(m, n) )
## [1] 9
result1(.func = function(x,y,z) sum(x,y,z) )
## [1] 14
result1(function(Time, demand) Time + demand, list2env(BOD))
## [1] 9.3 12.3 22.0 20.0 20.6 26.8
1a) Another possibility is to evaluate the body. This also works if envir is specified as a data frame whose columns are to be looked up.
result1a <- function(.func, envir = parent.frame()) {
eval(body(.func), envir)
}
result1a(.func = function(m,n) sum(m, n) )
## [1] 9
result1a(.func = function(x,y,z) sum(x,y,z) )
## [1] 14
result1a(function(Time, demand) Time + demand, BOD)
## [1] 9.3 12.3 22.0 20.0 20.6 26.8
2) Another design which is even simpler is to provide a one-sided formula interface. Formulas have environments so we can use that to look up the variables.
result2 <- function(fo, envir = environment(fo)) eval(fo[[2]], envir)
result2(~ sum(m, n))
## [1] 9
result2(~ sum(x,y,z))
## [1] 14
result2(~ Time + demand, BOD)
## [1] 9.3 12.3 22.0 20.0 20.6 26.8
3) Even simpler yet is to just pass the result of the computation as an argument.
result3 <- function(x) x
result3(sum(m, n))
## [1] 9
result3(sum(x,y,z))
## [1] 14
result3(with(BOD, Time + demand))
## [1] 9.3 12.3 22.0 20.0 20.6 26.8
This works.
#Custom function
result <- function(.func){
do.call(.func, lapply(formalArgs(.func), as.name))
}
#Data
m <- 1:3
n <- 0:2
x <- 1:2
y <- 0:1
z <- c(4,6)
#Call function
result(.func = function(m,n){ sum(m, n) })
result(.func = function(x,y,z){ sum(x,y,z) })
This seems like a bit of a pointless function, since the examples in your question imply that what you are trying to do is evaluate the body of the passed function using variables in the calling environment. You can certainly do this easily enough:
result <- function(.func){
eval(body(.func), envir = parent.frame())
}
This gives the expected results from your examples:
x <- 1:2
y <- 0:1
result(.func = function(x,y){ sum(x, y) })
#> [1] 4
and
m <- 1:3
n <- 0:2
x <- 1:2
y <- 0:1
z <- c(4,6)
result(.func = function(m,n){ sum(m, n) })
#> [1] 9
result(.func = function(x,y,z){ sum(x,y,z) })
#> [1] 14
But note that, when the user types:
result(.func = function(x,y){ ...user code... })
They get the same result they would already get if they didn't use your function and simply typed
...user code....
You could argue that it would be helpful with a pre-existing function like mean.default:
x <- 1:10
na.rm <- TRUE
trim <- 0
result(mean.default)
#> [1] 5.5
But this means users have to name their variables as the parameters being passed to the function, and this is just a less convenient way of calling the function.
It might be useful if you could demonstrate a use case where what you are proposing doesn't make the user's code longer or more complex.
You could also use ..., but like the other responses, I don't quite see the value, or perhaps I don't fully understand the use-case.
result <- function(.func, ...){
do.call(.func, list(...))
}
Create function
f1 <- function(a,b) sum(a,b)
Pass f1 and values to result()
result(f1, m,n)
Output:
[1] 9
Here is how I would do it based on your clarifying comments.
Basically since you say your function will take a data.frame as input, the function you are asking for essentially just reverses the order of arguments you pass to do.call()... which takes a function, then a list of arguments. A data.frame is just a special form of list where all elements (columns) are vectors of equal length (number of rows)
result <- function(.data, .func) {
# .data is a data.frame, which is a list of argument vectors of equal length
do.call(.func, .data)
}
result(data.frame(a=1, b=1:5), function(a, b) a * b)
result(data.frame(c=1:10, d=1:10), function(c, d) c * d)
I fear I get something really wrong. The basics are from here
and a basic (minimal) example is understood (I think) and working:
fun.default <- function(x) { # you could add further fun.class1 (works)...
print("default")
return(x[1] + x[2])
}
my_fun <- function(x) {
print("my_fun")
print(x)
res <- UseMethod("fun", x)
print(res)
print("END my_fun...")
return(res)
}
x <- c(1, 2)
my_fun(x)
However, if I want to add parameters, something goes really wrong. Form the link above:
Once UseMethod has found the correct method, it’s invoked in a special
way. Rather than creating a new evaluation environment, it uses the
environment of the current function call (the call to the generic), so
any assignments or evaluations that were made before the call to
UseMethod will be accessible to the method.
I tried all variants I could think of:
my_fun_wrong1 <- function(x, y) {
print("my_fun_wrong1")
print(x)
x <- x + y
print(x)
res <- UseMethod("fun", x)
print(res)
print("END my_fun_wrong1...")
return(res)
}
x <- c(1, 2)
# Throws: Error in fun.default(x, y = 2) : unused argument (y = 2)
my_fun_wrong1(x, y = 2)
my_fun_wrong2 <- function(x) {
print("my_fun_wrong2")
print(x)
x <- x + y
print(x)
res <- UseMethod("fun", x)
print(res)
print("END my_fun_wrong2...")
return(res)
}
x <- c(1, 2)
y = 2
# Does not throw an error, but does not give my expetced result "7":
my_fun_wrong2(x) # wrong result!?
rm(y)
my_fun_wrong3 <- function(x, ...) {
print("my_fun_wrong3")
print(x)
x <- x + y
print(x)
res <- UseMethod("fun", x)
print(res)
print("END my_fun_wrong3...")
return(res)
}
x <- c(1, 2)
# Throws: Error in my_fun_wrong3(x, y = 2) : object 'y' not found
my_fun_wrong3(x, y = 2)
Edit after answer G. Grothendieck: Using fun.default <- function(x, ...) I get
Runs after change, but I don't understand the result:
my_fun_wrong1(x, y = 2)
[1] "my_fun_wrong1"
[1] 1 2
[1] 3 4 # Ok
[1] "default"
[1] 3 # I excpect 7
As before - I don't understand the result:
my_fun_wrong2(x) # wrong result!?
[1] "my_fun_wrong2"
[1] 1 2
[1] 3 4 # Ok!
[1] "default"
[1] 3 # 3 + 4 = 7?
Still throws an error:
my_fun_wrong3(x, y = 2)
[1] "my_fun_wrong3"
[1] 1 2
Error in my_fun_wrong3(x, y = 2) : object 'y' not found
I think, this question is really useful!
fun.default needs ... so that the extra argument is matched.
fun.default <- function(x, ...) {
print("default")
return(x[1] + x[2])
}
x <- c(1, 2)
my_fun_wrong1(x, y = 2)
## [1] "my_fun_wrong1"
## [1] 1 2
## [1] 5 6
## [1] 3
Also, any statements after the call to UseMethod in the generic will not be evaluated as UseMethoddoes not return so it is pointless to put code after it in the generic.
Furthermore, you can't redefine the arguments to UseMethod. The arguments are passed on as they came in.
Suggest going over the help file ?UseMethod although admittedly it can be difficult to read.
Regarding the quote from ?UseMethod that was added to the question, this just means that the methods can access local variables defined in the function calling UseMethod. It does not mean that you can redefine arguments. Below ff.default refers to the a defined in ff.
a <- 0
ff <- function(x, ...) { a <- 1; UseMethod("ff") }
ff.default <- function(x, ...) a
ff(3)
## [1] 1
For example, I have a vector of functions: fun_vec <- c(step1,step2,step3).
Now I want to compose them like this: step1(step2(step3(x))). How do I do this using fun_vec? (Suppose that fun_vec isn't fixed and can have more or less functions.)
Similar to Frank's use of freduce, you can use Reduce:
step1 <- function(a) a^2
step2 <- function(a) sum(a)
step3 <- function(a) sqrt(a)
steps <- list(step1, step2, step3)
Reduce(function(a,f) f(a), steps, 1:3)
# [1] 3.741657
step3(step2(step1(1:3)))
# [1] 3.741657
You can see it "in action" with:
Reduce(function(a,f) f(a), steps, 1:3, accumulate=TRUE)
# [[1]]
# [1] 1 2 3
# [[2]]
# [1] 1 4 9
# [[3]]
# [1] 14
# [[4]]
# [1] 3.741657
You can use freduce from the magrittr package:
fun_vec = c(function(x) x^2, function(x) sum(x), function(x) sqrt(x))
library(magrittr)
freduce(1:10, fun_vec)
Alternately, define a function sequence with pipes like...
library(magrittr)
f = . %>% raise_to_power(2) %>% sum %>% sqrt
f(1:10)
A similar example: Is there a way to `pipe through a list'?
Here's a base R recursive approach:
compose <- function(funs) {
n <- length(funs)
fcomp <- function(x) funs[[n - 1]](funs[[n]](x))
ifelse(n > 2, compose(c(funs[1:(n - 2)], fcomp)), fcomp)
}
x <- c(sqrt, log, exp)
compose(x)(2)
# [1] 1.414214
sqrt(log(exp(2)))
# [1] 1.414214
If the number of functions in funs is greater than two, we shorten the list by one by replacing the last two functions by their composition. Otherwise, we return the composition of the last remaining two. It's assumed that initially there are at least two functions in funs.
Take a look at purrr::compose. If your functions are stored inside a list, use purrr::invoke to pass that list to compose:
fun_vec <- c( exp, log10, sqrt )
f <- purrr::invoke( purrr::compose, fun_vec )
f(4) # 1.35125
exp( log10( sqrt(4) ) ) # 1.35125
I have a function with default arguments which just returns its arguments as a list:
x <- function(a=1, b=2, c=3){formals()}
and another function which is a simple wrapper around x
y <- function(a, ...) x(a, ...)
However, in function x, I would like to get the runtime values of the arguments, not the default values. So that,
y(a=3, b=4)
should return
$a
[1] 3
$b
[1] 4
$c
[1] 3
and not
$a
[1] 1
$b
[1] 2
$c
[1] 3
which is what I get now. Maybe one of sys.* functions might help, but I am not so experienced on these... Is there an easy way to call a function like formals() to get the values of the arguments specified by the caller?
Maybe this?
x <- function(a=1, b=2, c=3) mget(names(formals()))
y <- function(a, ...) x(a, ...)
y(a=3, b=4)
#$a
#[1] 3
#
#$b
#[1] 4
#
#$c
#[1] 3