amanda
amanda

Reputation: 331

Use assign to create multiple functions inside of a function in R

I'd like to create a few functions that do slightly different things and that can each accept a global variable.

I use assign to assign the function this_value to the name it should have, this_key. The three functions it should create are func_a, func_b, and func_c.

Each newly minted function should be able to take an input x and do something particular to that function to x; in this case, print "key_name: name ----- value_x: x". So name should depend on whether the function is func_a, func_b, or func_c; e.g., calling func_a should give us "key_name: a".

to_name <- c("a", "b", "c")

create_funcs <- function() {
  all_funcs <- list()

  for (name in to_name) {
    this_key <- paste0("func_", name)

    this_value <<- function(x) { 
      paste0("key_name:  ", name, " -----  value_x: ", x)
    }
    assign(this_key, this_value, envir = .GlobalEnv) 

    all_funcs <- c(all_funcs, this_key)
  }
  return(all_funcs)
}

create_funcs()

However, after creating the functions, they each only return a name of "c", or the last value in the vector to_name.

func_a("foo")  # key_name:  c -----  value_x: foo
func_b("bar")  # key_name:  c -----  value_x: bar

Instead, func_a("foo") should return "key_name: a ----- value_x: foo".

For what it's worth, this works in a similar object-assigning function:

create_objects <- function() {
  all_objs <- list()

  for (name in to_name) {
    this_key <- paste0("key_", name)

    this_value <- paste0("value_", name)

    assign(this_key, this_value, envir = .GlobalEnv)

    all_objs <- c(all_objs, this_key)
  }
  return(all_objs)
}

create_objects()

Example:

key_a  # value_a

Thanks for taking a look!

Upvotes: 1

Views: 1531

Answers (1)

austensen
austensen

Reputation: 3017

I'm not entirely sure if this solves your problem, since your real use case is probably more complicated than this reproducible example. But you might want to check out purrr::partial.

Here's a possible solution I came up with using that function.

library(purrr)

func_ <- function(x, key_name) {
  paste0("key_name:  ", key_name, " -----  value_x: ", x)
}


func_a <- partial(func_, key_name = "a")

func_a("foo")

#> [1] "key_name:  a -----  value_x: foo"


assign("func_b", partial(func_, key_name = "b"))

func_b("foo")

#> [1] "key_name:  b -----  value_x: foo"

EDIT:

Here you can just take and character vector and iterate over each element (like with a for loop) using purrr::walk to create a new function for each one. You just have to make sure to set the environment to .GlobalEnv in the assign() call. I don't know that much about environment scoping for things like this, and feel like I've read that this can sometimes be a bad idea, but it seems to do what you described. Hope this helps.

letters %>% walk(~ assign(x = paste0("func_", .x),
                          value = partial(func_, key_name = .x),
                          envir = .GlobalEnv))

func_x("foo")

#> [1] "key_name:  x -----  value_x: foo"

Upvotes: 1

Related Questions