This question already has answers here:
Explain a lazy evaluation quirk
(2 answers)
Closed 7 years ago.
I am using lapply to make new functions and noticed that that sometimes it returns what is expected, and sometimes it returns only copies of the lastly created function.
Here is an example for the illustration, consider that I want to make the following simple list of functions
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
myfunc
})
Unfortunately, a simple evaluation shows that I am not getting what I hoped
listFuncs[[1]](10)
[1] 13
listFuncs[[2]](10)
[1] 13
Indeed, the list only contains the function
myfunc = function(y){3+y}
However, if I output something during the creation of the functions, for example
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
print(myfunc(0)) ## NEW LINE HERE !!!
myfunc
})
then my list of functions is "as expected"
[1] 1
[1] 2
[1] 3
> listFuncs[[1]](10)
[1] 11
> listFuncs[[2]](10)
[1] 12
Does anyone understand what is going on ? By advance, thank you.
You can use the force function:
listFuncs = lapply( 1:3,
function(X) {
force(X)
myfunc <- function(y) { X+y }
myfunc
}
)
listFuncs[[1]](10) ## 11
Related
In a for loop I make a "string-formula" and allocate it to e.g. body1. And when I try to make a function with that body1 it fails... And I have no clue what I should try else...
This question How to create an R function programmatically? helped me a lot but sadly only quote is used to set the body...
I hope you have an idea how to work around with this issue.
And now my code:
A.m=matrix(c(3,4,2,2,1,1,1,3,2),ncol=3,byrow=TRUE)
for(i in 1:dim(A.m)[1]) {
body=character()
# here the string-formula emerges
for(l in 1:dim(A.m)[2]) {
body=paste0(body,"A.m[",i,",",l,"]","*x[",l,"]+")
}
# only the last plus-sign is cutted off
assign(paste0("body",i),substr(body,1,nchar(body)-1))
}
args=alist(x = )
# just for your convenience the console output
body1
## [1] "A.m[1,1]*x[1]+A.m[1,2]*x[2]+A.m[1,3]*x[3]"
# in this code-line I don't know how to pass body1 in feasible way
assign("Function_1", as.function(c(args, ???body1???), env = parent.frame())
And this is my aim:
Function_1(x=c(1,1,1))
## 9 # 3*1 + 4*1 + 2*1
Since you have a string, you need to parse that string. You can do
assign("Function_1",
as.function(c(args, parse(text=body1)[[1]])),
env = parent.frame())
Though I would strongly discourage the use of assign for filling your global environment with a bunch of variables with indexes in their name. In general that makes things much tougher to program with. It would be much easier to collect all your functions in a list. For example
funs <- lapply(1:dim(A.m)[1], function(i) {
body <- ""
for(l in 1:dim(A.m)[2]) {
body <- paste0(body,"A.m[",i,",",l,"]","*x[",l,"]+")
}
body <- substr(body,1,nchar(body)-1)
body <- parse(text=body)[[1]]
as.function(c(alist(x=), body), env=parent.frame())
})
And then you can call the different functions by extracting them with [[]]
funs[[1]](x=c(1,1,1))
# [1] 9
funs[[2]](x=c(1,1,1))
# [1] 4
Or you can ever call all the functions with an lapply
lapply(funs, function(f, ...) f(...), x=c(1,1,1))
# [[1]]
# [1] 9
# [[2]]
# [1] 4
# [[3]]
# [1] 6
Although if this is actually what your function is doing, there are easier ways to do this in R using matrix multiplication %*%. Your Function_1 is the same as A.m[1,] %*% c(1,1,1). You could make a generator funciton like
colmult <- function(mat, row) {
function(x) {
as.numeric(mat[row,] %*% x)
}
}
And then create the same list of functions with
funs <- lapply(1:3, function(i) colmult(A.m, i))
Then you don't need any string building or parsing which tends to be error prone.
So consider the following chunk of code which does not work as most people might expect it to
#cartoon example
a <- c(3,7,11)
f <- list()
#manual initialization
f[[1]]<-function(x) a[1]+x
f[[2]]<-function(x) a[2]+x
f[[3]]<-function(x) a[3]+x
#desired result for the rest of the examples
f[[1]](1)
# [1] 4
f[[3]](1)
# [1] 12
#attempted automation
for(i in 1:3) {
f[[i]] <- function(x) a[i]+x
}
f[[1]](1)
# [1] 12
f[[3]](1)
# [1] 12
Note that we get 12 both times after we attempt to "automate". The problem is, of course, that i isn't being enclosed in the function's private environment. All the functions refer to the same i in the global environment (which can only have one value) since a for loop does not seem to create different environment for each iteration.
sapply(f, environment)
# [[1]]
# <environment: R_GlobalEnv>
# [[2]]
# <environment: R_GlobalEnv>
# [[3]]
# <environment: R_GlobalEnv>
So I though I could get around with with the use of local() and force() to capture the i value
for(i in 1:3) {
f[[i]] <- local({force(i); function(x) a[i]+x})
}
f[[1]](1)
# [1] 12
f[[3]](1)
# [1] 12
but this still doesn't work. I can see they all have different environments (via sapply(f, environment)) however they appear to be empty (ls.str(envir=environment(f[[1]]))). Compare this to
for(i in 1:3) {
f[[i]] <- local({ai<-i; function(x) a[ai]+x})
}
f[[1]](1)
# [1] 4
f[[3]](1)
# [1] 12
ls.str(envir=environment(f[[1]]))
# ai : int 1
ls.str(envir=environment(f[[3]]))
# ai : int 3
So clearly the force() isn't working like I was expecting. I was assuming it would capture the current value of i into the current environment. It is useful in cases like
#bad
f <- lapply(1:3, function(i) function(x) a[i]+x)
#good
f <- lapply(1:3, function(i) {force(i); function(x) a[i]+x})
where i is passed as a parameter/promise, but this must not be what's happening in the for-loop.
So my question is: is possible to create this list of functions without local() and variable renaming? Is there a more appropriate function than force() that will capture the value of a variable from a parent frame into the local/current environment?
This isn't a complete answer, partly because I'm not sure exactly what the question is (even though I found it quite interesting!).
Instead, I'll just present two alternative for-loops that do work. They've helped clarify the issues in my mind (in particular by helping me to understand for the first time why force() does anything at all in a call to lapply()). I hope they'll help you as well.
First, here is one that's a much closer equivalent of your properly function lapply() call, and which works for the same reason that it does:
a <- c(3,7,11)
f <- list()
## `for` loop equivalent of:
## f <- lapply(1:3, function(i) {force(i); function(x) a[i]+x})
for(i in 1:3) {
f[[i]] <- {X <- function(i) {force(i); function(x) a[i]+x}; X(i)}
}
f[[1]](1)
# [1] 4
Second, here is one that does use local() but doesn't (strictly- or literally-speaking) rename i. It does, though, "rescope" it, by adding a copy of it to the local environment. In one sense, it's only trivially different from your functioning for-loop, but by focusing attention on i's scope, rather than its name, I think it helps shed light on the real issues underlying your question.
a <- c(3,7,11)
f <- list()
for(i in 1:3) {
f[[i]] <- local({i<-i; function(x) a[i]+x})
}
f[[1]](1)
# [1] 4
Will this approach work for you?
ff<-list()
for(i in 1:3) {
fillit <- parse(text=paste0('a[',i,']+x' ))
ff[[i]] <- function(x) ''
body(ff[[i]])[1]<-fillit
}
It's sort of a lower-level way to construct a function, but it does work "as you want it to."
An alternative for force() that would work in a for-loop local environment is
capture <- function(...) {
vars<-sapply(substitute(...()), deparse);
pf <- parent.frame();
Map(assign, vars, mget(vars, envir=pf, inherits = TRUE), MoreArgs=list(envir=pf))
}
Which can then be used like
for(i in 1:3) {
f[[i]] <- local({capture(i); function(x) a[i]+x})
}
(Taken from the comments in Josh's answer above)
This question already has answers here:
Enclosing variables within for loop
(3 answers)
Closed 4 years ago.
I am trying to create a list of functions where each function is slightly different. The following code works fine:
fun1 <- function(n) {
fun2 <- function(x) {
x^n
}
return(fun2)
}
powerfuns <- vector("list", 3)
powerfuns[[2]] <- fun1(2)
powerfuns[[3]] <- fun1(3)
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64
The second element of the list is a function that squares its argument while the third is a function that cubes its argument. But it doesn't seem to work if I create the list using a for-loop instead:
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 64
powerfuns[[3]](4)
# [1] 64
Now both functions cube their arguments. There seems to be something about the for-loop that causes the environments for the functions to be identical. get("n", environment(powerfuns[[2]])) returns 3 in the second example but 2 in the first example. Does anyone have a suggestion about how I can get the result I want using a loop or something similar? Thanks!
This has to do with lazy evaulation. Since fun1 doesn't use the value n you pass in right away, it doesn't capture the value in the way you expect. You can fix this with
fun1 <- function(n) {
force(n)
fun2 <- function(x) {
x^n
}
return(fun2)
}
Then you can run the same code
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64
This question already has answers here:
Usage of local variables
(2 answers)
Closed 5 years ago.
In C and variants, when you have something like this:
{
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
}
a 7 will be printed, but the tmp1 and tmp2 variables will be removed from stack once the scope } ends. I sometimes want similar functionality in R so that I do not have to clean up (many) temporary variables after some point in time.
One way to make this work in R is like this:
(function(){
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})()
Now that seems a bit hackish -- or it may be just me.
Are there any better or more concise (i.e. more readable) ways to do this in R?
You might want to use local in base R for that purpose:
local({
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})
So any variable created within the scope of local will vanish as soon as the scope is left.
From ?local:
local evaluates an expression in a local environment.
See ?local for more details.
Additionally, with (suggested by #Rich Scriven in the comments) also in base R can be used where 1 is just a dummy data:
with(1, {
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})
From ?with:
with is a generic function that evaluates expr in a local environment constructed from data.
Within the main code body the variables are global. However, as you found yourself their scope is local within functions:
addme <- function(){
tmp1 <- 5;
tmp2 <- 2;
return (tmp1+tmp2)
}
then call calling addme() will return the sum, but the variable remain inaccessible outside
> addme()
[1] 7
> tmp1
Error: object 'tmp1' not found
This question already has answers here:
How to curry a ... argument by position in R?
(2 answers)
Closed 9 years ago.
I need to create additional name for my_function(i,x) (where i can be an integer from 1 to 25). I'd like it to work like this
my_function1(x) sames as my_function(1,x)
my_function2(x) sames as my_function(2,x)
my_function3(x) sames as my_function(3,x)
...
my_function25(x) sames as my_function(25,x)
One way to do achieve this would be:
my_function1 <- function (x) my_function(1, x)
my_function2 <- function (x) my_function(2, x)
my_function3 <- function (x) my_function(3, x)
...
But since there are 25 of them it would be reasonable to make it in a loop. For this I've tried:
for(i in 1:25){
assign(paste("my_function",i,sep=""),function(x) my_function(i,x))
}
but it doesn't work since i is passed by reference and in the end the result was
my_function1(x) sames as my_function(25,x)
my_function2(x) sames as my_function(25,x)
my_function3(x) sames as my_function(25,x)
...
How can I pass "i" by value? Or perhaps there is some other way...
Why would I want to do this? I'm improving someones else R package in terms of efficiency but at the same time I need it to be compatible with old version.
This is called currying, and is a part of functional programming.
library(functional)
myf <- function(a,x) cat(a,x,"\n")
myf1 <- Curry(myf, a=1)
myf1(5)
for(i in seq(25)) assign(paste0("myf",i), Curry(myf,a=i) )
> myf15(5)
15 5
I guess there's an important question here as to why you'd want to do this. This seems like exactly the kind of thing you'd want arguments not many related functions for.
Well, you can achieve the same result, using base functions too.
The trick is to force (force) the evaluation of i at each iteration and assign your function in the .Globalenv (or the environment you like)
my_function <- function(a, b) a + b
lapply(1:10, function(i) {
force(i)
assign(paste0("my_function", i), function(x) my_function(i, x), envir = .GlobalEnv)
}
)
my_function1(10)
## [1] 11
my_function9(10)
## [1] 19
I think bquote will help here:
for(i in 1:2){
assign(paste("my_function",i,sep=""), bquote( function(x) my_function( i = .(i) , x ) ) )
}
>my_function2
# function(x) my_function(i = 2L, x)
But the point still stands - why would you want to do this?