>From the code below:y <- 1 f1 <- function() { cat("y =", y, "\n") } f2 <- function() { y <- 2 f1() } f3 <- function() { y <- 3 f <- f1 f() } f4 <- function() { y <- 4 f <- function() { cat("y =", y, "\n") } f() } f1() f2() f3() f4() Clearly, f1(), f2() and f4() will display "y = 1", "y = 1" and "y = 4", but, not as much clearly but predictably, f3() also displays "y = 1". Is there any way to rewrite the code of f3 in such a way that it displays "y = 3"? An obvious but cumbersome way would be something like: f3 <- function() { y <- 3 # write the code of f1 to a temporary file dump("f1", "temp.R") # read the code of f1 str <- readLines("temp.R") # replace the code that defines function f1 for a code that defines function f str <- gsub("f1 <-", "f <-", str) # write the new code to the temporary file writeLines(str, "temp.R") # read the source but use local to get things from f3's environment # (with the default local = FALSE, "y" would get the value from globalenv()) source("temp.R", local = TRUE) # ...? f() # PROFIT! } Is there a more elegant way to do this? Alberto Monteiro
On 23/10/2015 11:08 AM, ALBERTO VIEIRA FERREIRA MONTEIRO wrote:> From the code below: > > y <- 1 > > f1 <- function() { > cat("y =", y, "\n") > } > > f2 <- function() { > y <- 2 > f1() > } > > f3 <- function() { > y <- 3 > f <- f1 > f() > } > > f4 <- function() { > y <- 4 > f <- function() { cat("y =", y, "\n") } > f() > } > > f1() > f2() > f3() > f4() > > Clearly, f1(), f2() and f4() will display "y = 1", "y = 1" and "y = 4", > but, not as much clearly but predictably, f3() also displays "y = 1". > > Is there any way to rewrite the code of f3 in such a way that it > displays "y = 3"?After f <- f1, you can say environment(f) <- environment() That says that f should resolve non-local symbols by looking in the environment that is current in that line, i.e. the local evaluation frame of this invocation of f3. One warning: if in the real use case, the user is supplying f1, then the user might be giving you something where the environment really matters, and this substitution would cause the function to return nonsense. Or if the user's f1 makes use of other global variables that happen to have the same name as other locals in f3, nonsense again. Duncan Murdoch> > An obvious but cumbersome way would be something like: > > f3 <- function() { > y <- 3 > # write the code of f1 to a temporary file > dump("f1", "temp.R") > # read the code of f1 > str <- readLines("temp.R") > # replace the code that defines function f1 for a code that defines function f > str <- gsub("f1 <-", "f <-", str) > # write the new code to the temporary file > writeLines(str, "temp.R") > # read the source but use local to get things from f3's environment > # (with the default local = FALSE, "y" would get the value from globalenv()) > source("temp.R", local = TRUE) > # ...? > f() > # PROFIT! > } > > Is there a more elegant way to do this? > > Alberto Monteiro > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >
I do not understand exactly what you're looking for: "Any way to rewrite the code.." is pretty vague. Here is _an_ answer, which may completely miss what you mean, followed by some comments. y <- 1 f1 <- function(x=y) { cat("y =", x, "\n") } f2 <- function() { y <- 2 f1() } f3 <- function() { y <- 3 f1(y) } f4 <- function() { y <- 4 f <- function() { cat("y =", y, "\n") } f() }> f1()y = 1> f2()y = 1> f3()y = 3> f4()y = 4 Following up on Duncan's comments: 1) Looking up free variables in the enclosing environment is always potentially risky imo, although it can certainly be useful. Explicitly passing arguments with defaults seems safer. 2) Explicitly changing the enclosing environment is even riskier, as Duncan made clear. The "environment<-" Help file explicitly adds: "The replacement function parent.env<- is extremely dangerous as it can be used to destructively change environments in ways that violate assumptions made by the internal C code. It may be removed in the near future" If neither Duncan's nor my suggestions are helpful, you might try re-posting with more context about the specific use case you are concerned with. Cheers, Bert Bert Gunter "Data is not information. Information is not knowledge. And knowledge is certainly not wisdom." -- Clifford Stoll On Fri, Oct 23, 2015 at 8:21 AM, Duncan Murdoch <murdoch.duncan at gmail.com> wrote:> On 23/10/2015 11:08 AM, ALBERTO VIEIRA FERREIRA MONTEIRO wrote: >> From the code below: >> >> y <- 1 >> >> f1 <- function() { >> cat("y =", y, "\n") >> } >> >> f2 <- function() { >> y <- 2 >> f1() >> } >> >> f3 <- function() { >> y <- 3 >> f <- f1 >> f() >> } >> >> f4 <- function() { >> y <- 4 >> f <- function() { cat("y =", y, "\n") } >> f() >> } >> >> f1() >> f2() >> f3() >> f4() >> >> Clearly, f1(), f2() and f4() will display "y = 1", "y = 1" and "y = 4", >> but, not as much clearly but predictably, f3() also displays "y = 1". >> >> Is there any way to rewrite the code of f3 in such a way that it >> displays "y = 3"? > > After f <- f1, you can say > > environment(f) <- environment() > > That says that f should resolve non-local symbols by looking in the > environment that is current in that line, i.e. the local evaluation > frame of this invocation of f3. > > One warning: if in the real use case, the user is supplying f1, then > the user might be giving you something where the environment really > matters, and this substitution would cause the function to return > nonsense. Or if the user's f1 makes use of other global variables that > happen to have the same name as other locals in f3, nonsense again. > > Duncan Murdoch > >> >> An obvious but cumbersome way would be something like: >> >> f3 <- function() { >> y <- 3 >> # write the code of f1 to a temporary file >> dump("f1", "temp.R") >> # read the code of f1 >> str <- readLines("temp.R") >> # replace the code that defines function f1 for a code that defines function f >> str <- gsub("f1 <-", "f <-", str) >> # write the new code to the temporary file >> writeLines(str, "temp.R") >> # read the source but use local to get things from f3's environment >> # (with the default local = FALSE, "y" would get the value from globalenv()) >> source("temp.R", local = TRUE) >> # ...? >> f() >> # PROFIT! >> } >> >> Is there a more elegant way to do this? >> >> Alberto Monteiro >> >> ______________________________________________ >> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see >> https://stat.ethz.ch/mailman/listinfo/r-help >> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html >> and provide commented, minimal, self-contained, reproducible code. >> > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.
I have another environment question. I understand why this works as expected: f.factory <- function() { y <- 2 fname <- paste("plus", y, sep = ".") f <- function(x) x + y assign(fname, f, envir = globalenv()) } f.factory() plus.2(2) # 4 and I also understand why this does NOT work: f.factory <- function() { for (y in 2:3) { fname <- paste("plus", y, sep = ".") f <- function(x) x + y assign(fname, f, envir = globalenv()) } } f.factory() plus.2(2) # 5 (the reason is that both plus.2 and plus.3 have the same environment as f.factory when f.factory terminates, which assign 2 to variable y in the first case and 3 in the second case) However, I don't know an elegant way to adapt f.factory so that it works as expected. Any suggestions? Alberto Monteiro
Make a new environment for each function and populate it with the variables that your functions require. local() is a convenient way to do this: f.factory3 <- function(destinationEnvir = globalenv()) { for (y in 2:3) { fname <- paste("plus", y, sep = ".") f <- local(function(x) x + y, envir=list2env(list(y=y), parent=destinationEnvir)) assign(fname, f, envir = destinationEnvir) } } Bill Dunlap TIBCO Software wdunlap tibco.com On Fri, Nov 13, 2015 at 9:53 AM, ALBERTO VIEIRA FERREIRA MONTEIRO <albmont at centroin.com.br> wrote:> I have another environment question. > > I understand why this works as expected: > > f.factory <- function() > { > y <- 2 > fname <- paste("plus", y, sep = ".") > f <- function(x) x + y > assign(fname, f, envir = globalenv()) > } > > f.factory() > plus.2(2) # 4 > > and I also understand why this does NOT work: > > f.factory <- function() > { > for (y in 2:3) { > fname <- paste("plus", y, sep = ".") > f <- function(x) x + y > assign(fname, f, envir = globalenv()) > } > } > > f.factory() > plus.2(2) # 5 > > (the reason is that both plus.2 and plus.3 have the > same environment as f.factory when f.factory terminates, > which assign 2 to variable y in the first case and 3 in > the second case) > > However, I don't know an elegant way to adapt f.factory > so that it works as expected. Any suggestions? > > Alberto Monteiro > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.
On 13/11/2015 12:53 PM, ALBERTO VIEIRA FERREIRA MONTEIRO wrote:> I have another environment question. > > I understand why this works as expected: > > f.factory <- function() > { > y <- 2 > fname <- paste("plus", y, sep = ".") > f <- function(x) x + y > assign(fname, f, envir = globalenv()) > } > > f.factory() > plus.2(2) # 4 > > and I also understand why this does NOT work: > > f.factory <- function() > { > for (y in 2:3) { > fname <- paste("plus", y, sep = ".") > f <- function(x) x + y > assign(fname, f, envir = globalenv()) > } > } > > f.factory() > plus.2(2) # 5 > > (the reason is that both plus.2 and plus.3 have the > same environment as f.factory when f.factory terminates, > which assign 2 to variable y in the first case and 3 in > the second case) > > However, I don't know an elegant way to adapt f.factory > so that it works as expected. Any suggestions?I think Bill answered your question. I'd suggest that you should ask a different question. It's generally a bad idea to use assign(), especially for assignments into the global environment. Your f.factory should return a value, it shouldn't modify globalenv(). It's often a bad idea to refer to the environment explicitly at all. So my recommended solution might be this variation on Bill's: f.factory4 <- function() { result <- list() for (y in 2:3) { result[[y]] <- local({ ylocal <- y function(x) x + ylocal }) names(result)[y] <- paste("plus", y, sep = ".") } result } The "names" line is optional; with it, you can use f <- f.factory4() f$plus.2(2) but with or without it you can use f[[2]](2) Duncan Murdoch