0

I have two functions nested in another function and I would like that the arguments declared in parent were passed to the child (only when the argument is relevant to the function).

# child function 1 child_f1 <- function(x1 = 1, x2 = 3) { res <- x1^2 + 4 * x2 } # child function 2 child_f2 <- function(z = 2) { res <- z * 1.345 } # parent function parent_f <- function(y = 4, ...) { res <- (child_f1(...) ^ y) + child_f2(...) # print(res) return(res) } 

Test below:

parent_f(y = 2, x1 = 2, x2 = 0, z = 3) # Error in child_f1(...) (from #2) : unused argument (z = 3) # Expected result: (((2)^2 + 4*(0)) ^ (2)) + (3) * 1.345 [1] 20.04 

How do I tell child_f1 that has to use only x1 and x2 (if available, otherwise use default value) and child_f2 that has to use only z(if available, otherwise use default value)?

I would like to stick with the usage of ... rather than re-write a parent_f() with all the possible parameters declared.

3 Answers 3

2

Add ... to each child to slurp up the unused arguments. parent_f is unchanged.

child_f1 <- function(x1 = 1, x2 = 3, ...) { res <- x1^2 + 4 * x2 } child_f2 <- function(z = 2, ...) { res <- z * 1.345 } parent_f(y = 2, x1 = 2, x2 = 0, z = 3) ## [1] 20.035 

If you can't modify the children or the parent then use this:

child_f1_orig <- child_f1 child_f1 <- function(x1 = 1, x2 = 3, ...) child_f1_orig(x1 = x1, x2 = x2) child_f2_orig <- child_f2 child_f2 <- function(z = 2, ...) child_f2_orig(z = z) parent_f(y = 2, x1 = 2, x2 = 0, z = 3) ## [1] 20.035 
Sign up to request clarification or add additional context in comments.

3 Comments

Very interesting - and I think this is also the answer that carries the less invasive solution.
I like this as well, as long as you have control over the child functions. If not, then the formals-approach may be the only practical option.
Have added an option if you can't modify parent or children.
2

We can use formals to reduce the arguments to those supported by the child function. This relies on all arguments being named, which is supported here. After that, we use do.call to programmatically call a function with a list of arguments.

# parent function parent_f <- function(y = 4, ...) { dots <- list(...) dots1 <- dots[intersect(names(dots), names(formals(child_f1)))] dots2 <- dots[intersect(names(dots), names(formals(child_f2)))] res <- (do.call(child_f1, dots1) ^ y) + do.call(child_f2, dots2) # print(res) return(res) } parent_f(y = 2, x1 = 2, x2 = 0, z = 3) # [1] 20.035 

2 Comments

Thanks for the answer. It works for me. I tested the same function removing all (or some of) the arguments and the calculation uses (as you would expect) the default values. Brilliant solution.
I have accepted the answer from another user as it requires the minimum effort, but really thank you for this - I will definitely bear in mind the usage of formals in the future.
1

You can make use of formal and design a proper function for your usecase

use_fun_with_args <- function(fun, ...) { dots <- list(...) do.call(fun, args = dots[intersect(names(formals(fun)), names(dots))]) } # parent function parent_f <- function(y = 4, ...) { res <- (use_fun_with_args(fun = child_f1, ...) ^ y) + use_fun_with_args(fun = child_f2, ...) # print(res) return(res) } parent_f(y = 2, x1 = 2, x2 = 0, z = 3) #> [1] 20.035 

2 Comments

I think this will mis-assign NA where the formals have default values and dots does not include it. For instance, try with parent_f(y = 2, x1 = 2, z = 3), where the removed x2 has a default value in child_f1 that should be used.
good spot! I adopted the function, basically, mimicing your logik. Not much of an improvement to your solution though besides having it functionlised

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.