0
votes

I would like to use the calculations from high-level (outer) function high_lvl_fun in in a low-level (inner) function low_lvl_fun. The low-level function is an argument of the high-level one (I would like to use different functions with different sets of arguments). My reproducible example:

set.seed(101)

low_lvl_fun <- function(x, y){ # low-level (inner) function
  sum((x-y)^2) # Mean Squared Error
}

high_lvl_fun <- function(x, y = NULL, FUN, args){ # high level (outer) function

# Just some toy changes in y to check if the code works
  if(length(y) == 0){
    y <- rep(1, length(x))
  }else{
    y <- rep(2, length(x))
  }

  do.call(FUN, args = args) # Call of low_lvl_fun
}

The low-level function computes Mean Squared Error. The high-level function performs some operations on vector y and calls the low-level function. Declaration of such an argument and the high-level function call:

x <- rnorm(100)
high_lvl_fun(x, y = NULL, FUN = "low_lvl_fun", args = list(x, y))

results in such an error:

Error in do.call(FUN, args = args) : object 'y' not found

I understand that the low-level function assumes that the value of y is NULL (as declared in high-level function call), however, I don't know how to change the scope in which the low-level function searches for y.

The only solution I came up with would be to declare y in the global environment:

high_lvl_fun2 <- function(x, y = NULL, FUN, args){ # high level (outer) function

  if(length(y) == 0){
    y <<- rep(1, length(x))
  }else{
    y <<- rep(2, length(x))
  }

  do.call(FUN, args = args) # Call of low_lvl_fun
}

however, I would like to avoid modifying y in the global environment.

EDIT: (more details)

The low-level function can take arguments other than x and y. It may also require only x and other arguments, and not y, for example:

low_lvl_fun2 <- function(x){sd(x)/mean(x)}

The other important thing is that high and low-level functions can have the arguments with the same names (like above, where both functions have arguments called x and y) and it would be good not being forced to rewrite low-level function. Unfortunately, the implementation in the comments suggested by @Andrea does not meet this condition, since matching two arguments with the same names throws an error:

high_lvl_fun <- function(x, y = NULL, FUN, ...){ # Function suggested by @Andrea

  dots <- lazy_eval(lazy_dots(...))

  # Just some toy changes in y to check if the code works
  if(length(y) == 0){
    y <- rep(1, length(x))
  }else{
    y <- rep(2, length(x))
  }

  args <- c(list(x , y) , dots)

  do.call(FUN, args = args) # Call of low_lvl_fun
}

# Calling the low-level function at the beginning of the post
high_lvl_fun(x = 1:10, y = 2:11, FUN = "low_lvl_fun", x = x, y = y)

Error in high_lvl_fun(x = 1:10, y = 2:11, FUN = "low_lvl_fun", x = x, : formal argument "x" matched by multiple actual arguments

2

2 Answers

0
votes

Assuming that low_lvl_fun() takes x and y only. This should do the job

high_lvl_fun <- function(x, y = NULL, FUN ){ # high level (outer) function

  # Just some toy changes in y to check if the code works
  if(length(y) == 0){
    y <- rep(1, length(x))
  }else{
    y <- rep(2, length(x))
  }

  args <- list(x = x, y = y)


  do.call(FUN, args = args) # Call of low_lvl_fun
}
0
votes

As a more general solution I would suggest The use of the ... argument

require(lazyeval)

high_lvl_fun <- function(x, y = NULL, FUN, ...){ # high level (outer) function

  dots <- lazy_eval(lazy_dots(...))

  # Just some toy changes in y to check if the code works
  y <- y+1

  args <- c(list(x , y) , dots)

  do.call(FUN, args = args) # Call of low_lvl_fun
}

# Ex 1
f <- function(x, y , z) {x+y+z}
high_lvl_fun (x = 1, y = 2,  FUN = f, z = 3) 

# Ex 2
g <- function(x, y , z, mean , sd) {
  n <- x+y+z
  sum(rnorm(n , mean , sd))

}
high_lvl_fun (x = 1, y = 2,  FUN = g, z = 3, mean = 100, sd = 1)