1

Hi I like to create a list of functions dynamically. The only thing is that I have stored variables that I like to input as my function. For example, say I have a named vector with color.

color = c("blue","red")
names ( color) = c("a","b")

Now I want to create a list of dynamic function returning the color.

c = list()

for ( n in names ( color)){
    c[[n]]= function ( x){
        return ( color[n])
    }
}

however as you can see it returns "color[n]" and not the evaluated value.

> c$a
function ( x){
        return ( color[n])
    }
<bytecode: 0x0000000105e0a108>
Ahdee
  • 4,679
  • 4
  • 34
  • 58
  • What's your end goal here? to get the names of the colours in to a list? `as.list(names(color)` would achieve this. – Jonny Phelps Mar 04 '20 at 17:08
  • 1
    @JonnyPhelps no that was just an example. I need to create functions that is specific to each round of the loop. Color was just an example I came up with. – Ahdee Mar 04 '20 at 17:10
  • Use `c$a()`? Returns `"red"`. – M. Papenberg Mar 04 '20 at 17:15
  • 1
    For what reason? I'm struggling to think of a practical use case as you can usually achieve these results by making one function and then looping the function over the variable set e.g. with `lapply` https://www.guru99.com/r-apply-sapply-tapply.html – Jonny Phelps Mar 04 '20 at 17:15
  • @M.Papenberg yes but it's just finding `color` in the global environment. Change `color[2] = "hi"` and `c[[2]]()` will now return `"hi"`. – Gregor Thomas Mar 04 '20 at 17:17

4 Answers4

2

This answer uses the eval(parse(…)) construct, which has its flaws (What specifically are the dangers of eval(parse(...))?). But you could try:

for (n in names(color)) {
  c[[n]] <- eval(parse(text = sprintf("function(x) return(color['%s'])", n)))
}

c
# $`a`
# function (x) 
# return(color["a"])

# $b
# function (x) 
# return(color["b"])

c$a()
#      a 
# "blue" 

But as commenters (to your question, and elsewhere on SO) have suggested, there may be more elegant solutions to your problem, depending on your context and use case.

Weihuang Wong
  • 12,868
  • 2
  • 27
  • 48
1

EDIT

I have realised based on the above answers that you want to have a function where you then probably want to pass further arguments.

Here is the code with eval(expr())-statement. It's maybe better readable than the above answers, but not really different.

c = list()
for ( n in names (color)){
  c[[n]]= eval(expr(function(x) {return(color[(!!n)])}))
}
MKR
  • 1,620
  • 7
  • 20
1

Try something like this:

color = c("blue","red")
names ( color) = c("a","b")

c = list()

for ( n in names ( color)){

  eval(
    parse(
      text = paste0(
        "
c[[n]]= function ( x){
    return ('", color[n],"')
  }"
      )
    )
  )
}
c$a
#> function ( x){
#>     return ('blue')
#>   }

Created on 2020-03-04 by the reprex package (v0.3.0)

Rohit
  • 1,967
  • 1
  • 12
  • 15
1

Here's one method:

c = list()
for ( n in names ( color)) {
  c[[n]] <- (function(n) {
    force(n)
    function (x) return ( color[n])
  })(n)
}

Note this creates functions as you requested, which means you need to call then with () to get the values returned. For example

c$a()
#      a 
# "blue" 
c$b()
#     b 
# "red" 

Or it's even easier if you replace the for loop withMap`

c <- Map(function(n) {
  function(x) color[n]
}, names(color)) 

c$a()
#      a 
# "blue" 
c$b()
#     b 
# "red" 
MrFlick
  • 195,160
  • 17
  • 277
  • 295