Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create list of functions without eval/parse

Tags:

r

I have 3 vectors of equal length y, h and hp defined as follows:

y  <- c(2, 5, 6)
h  <- c(4, 25, 35)
hp <- c(3, 10, 12)

The values are simply illustrative.

I want to create an output list final_list of functions in x as follows

function(x) y + (h - hp) * x

(only ideal illustrative output shown):

[[1]]
[1] function(x) 2 + (1) * x

[[2]]
[1] function(x) 5 + (15) * x

[[3]]
[1] function(x) 6 + (23) * x

I am aware that this can be done with eval/parse, but this does not produce transparent output for the functions.

I would like to create the functions from these 3 vectors and output without using eval/parse. If this is possible I would be really happy to learn and be impressed!

like image 729
user4687531 Avatar asked Dec 12 '15 01:12

user4687531


3 Answers

You can use Map() with substitute(). The middle expressions are not yet evaluated, but I don't think that's such a big deal. They will be evaluated when the functions are called. Basically we just assemble the function in parts.

funs <- Map(
    function(a, b, c) {
        f <- function(x) x
        body(f) <- substitute(y + (h - hp) * x, list(y = a, h = b, hp = c))
        f
    }, 
    a = y, b = h, c = hp
)

funs
# [[1]]
# function (x) 
# 2 + (4 - 3) * x
# <environment: 0x4543fd0>
#
# [[2]]
# function (x) 
# 5 + (25 - 10) * x
# <environment: 0x4549e20>
#
# [[3]]
# function (x) 
# 6 + (35 - 12) * x
# <environment: 0x454e5d8>

Now let's call the functions -

sapply(funs, function(a) a(1))
# [1]  3 20 29

Note: If you really need those middle expressions evaluated in the function bodies, you can use the following instead.

make <- function(a, b, c) {
    d <- b - c
    f <- function(x) x
    body(f) <- substitute(y + (e) * x, list(y = a, e = d))
    f
}

funs <- Map(make, y, h, hp)
like image 79
Rich Scriven Avatar answered Oct 14 '22 19:10

Rich Scriven


y <- c(2,5,6)
h <- c(4, 25, 35)
hp <- c(3, 10, 12)
fun_create <- function(y, h, hp){
  fun <- function(x){y + (h - hp)*x}
  return(fun)
}
out <- mapply(y, h, hp, FUN = fun_create)

The output doesn't give what you might expect but it works correctly:

> out
[[1]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x282ee40>

[[2]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x282e610>

[[3]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x282dde0>

> out[[1]](1)
[1] 3
like image 29
Dason Avatar answered Oct 14 '22 18:10

Dason


Just using the function-function will succeed if it is executed in the correct environment.

> mapply( function(y,h,hp) function(x){ y+(h-hp)*x }, y,h,hp)
[[1]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x7fb570828710>

[[2]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x7fb570823718>

[[3]]
function (x) 
{
    y + (h - hp) * x
}
<environment: 0x7fb57081b5c8>


> myfuns[[1]](x=1:10)
 [1]  3  4  5  6  7  8  9 10 11 12
> 2+(h[1]-hp[1])*1:10
 [1]  3  4  5  6  7  8  9 10 11 12

> myfuns[[2]](x=1:10)
 [1]  20  35  50  65  80  95 110 125 140 155

Each of those function definitions (actually closures) carries along the first matching values that existed at the time of its creation when the interpreted traveled along the search path.

> environment(myfuns[[1]])[["y"]]
[1] 2
> environment(myfuns[[1]])[["h"]]
[1] 4
> environment(myfuns[[1]])[["hp"]]
[1] 3
like image 22
IRTFM Avatar answered Oct 14 '22 19:10

IRTFM