I'm going to n-th Duncan's recommendations to try to keep your functions small and try not to mess with environments.
... So I'm ashamed that I wrote the following - I apologize in advance. But as an intellectual exercise, we can cannibalize the code for `dynGet` and create a `dynSet` function which mirrors it: --- dynSet <- function (x, value, ifnotfound = stop(gettextf("%s not found", sQuote(x)), domain = NA), minframe = 1L, inherits = FALSE) { n <- sys.nframe() myObj <- structure(list(.b = as.raw(7)), foo = 47L) while (n > minframe) { n <- n - 1L env <- sys.frame(n) r <- get0(x, envir = env, inherits = inherits, ifnotfound = myObj) if (!identical(r, myObj)) { assign(x, value, envir = env) return(NULL) } } ifnotfound } ### Note that in bar1 / bar2, you can't use x,y,z - dynSet will match the local variable instead of the calling frame. bar1 <- function(){ x1 <- 1 y1 <- 1 z1 <- 1 cat(sprintf('bar1: x=%d, y=%d, z=%d\n', x1, y1, z1)) dynSet("x", x1) dynSet("y", y1) dynSet("z", z1) } bar2 <- function(){ x2 <- 2 y2 <- 2 z2 <- 2 cat(sprintf('bar2: x=%d, y=%d, z=%d\n', x2, y2, z2)) dynSet("x", x2) dynSet("y", y2) dynSet("z", z2) } foo <- function(a=1, b=2, c=0){ # some setup code dummy <- a + b x <- y <- z <- 0 # here is my scope problem if (c==1) bar1() if (c==2) bar2() # some more code cat(sprintf('foo: x=%d, y=%d, z=%d\n', x, y, z)) } foo(c=0) foo(c=1) foo(c=2) --- Please don't actually do this though. I think the most educational part of this is the dynGet/dynSet function itself - if you understand the various functions it uses, you can do pretty much anything in R. But again, you shouldn't. On Tue, Oct 30, 2018 at 1:51 PM Duncan Murdoch <murdoch.dun...@gmail.com> wrote: > On 30/10/2018 4:18 PM, Sebastien Bihorel wrote: > > Thanks Duncan for your quick reply. > > > > Ideally, I would want bar1 and bar2 to be independent functions, because > they are huge in actuality and, as the actual foo function grows, I may end > up with 10 different bar# functions. So I would like to separate them from > foo as much as possible. > > If that's the case, then I think the second solution (passing around > environments) is a really bad idea. Functions should not have side > effects because it makes them harder to understand. Modifying local > variables in some other function is a really dangerous side effect, > especially if both functions are big, because they are already hard to > understand. > > If you really have more callers for bar1() than just foo(), it is even > worse. > > So I'd suggest having bar1 and bar2 return the new values in a list, and > in foo(), explicitly extract the values you want from the list. Then if > in the future you decide that bar1 should also return a 4th value w, or > you want to rename x to something more meaningful, you don't need to > check your other foo functions to see if x and w are used in the same > way in them. They'll just ignore w if it isn't relevant to them. Your > foo() code becomes something like this: > > x <- y <- z <- 0 > > # here is my scope problem > result <- list(x = x, y = y, z = z) # c == 0 case > if (c==1) result <- bar1() > if (c==2) result <- bar2() > x <- result[["x"]] > y <- result[["y"]] > z <- result[["z"]] > > Duncan Murdoch > > > > > > > > ----- Original Message ----- > > From: "Duncan Murdoch" <murdoch.dun...@gmail.com> > > To: "Sebastien Bihorel" <sebastien.biho...@cognigencorp.com>, > r-help@r-project.org > > Sent: Tuesday, October 30, 2018 4:13:05 PM > > Subject: Re: [R] Question about function scope > > > > On 30/10/2018 3:56 PM, Sebastien Bihorel wrote: > >> Hi, > >> > >> From the R user manual, I have a basic understanding of the scope of > function evaluation but have a harder time understanding how to mess with > environments. > >> > >> My problem can be summarized by the code shown at the bottom: > >> - the foo function performs some steps including the assignment of > default values to 3 objects: x, y, z > >> - at some point, I would like to call either the bar1 or bar2 function > based upon the value of the c argument of the foo function. These functions > assign different values to the x, y, z variables. > >> - then foo should move on and do other cool stuff > >> > >> Based upon default R scoping, the x, y, and z variables inside the bar1 > and bar2 functions are not in the same environment as the x, y, and z > variables created inside the foo function. > >> > >> Can I modify the scope of evaluation of bar1 and bar2 so that x, y, and > z created inside the foo function are modified? > >> > >> PS: > >> - I know about "<<-" but, in my real code (which I cannot share, > sorry), foo is already called within other functions and x, y, and z > variables do not exist in the top-level environment and are not returned by > foo. So "<<-" does not work (per manual: " Only when <<- has been used in a > function that was returned as the value of another function will the > special behavior described here occur. ") > > > > I haven't looked up that quote, but it is likely describing a situation > > that isn't relevant to you. For you, the important part is that bar1 > > and bar2 must be created within foo. They don't need to be returned > > from it. > > > > So my edit below of your code should do what you want. > > > > foo <- function(a=1, b=2, c=0){ > > > > bar1 <- function(){ > > x <<- 1 > > y <<- 1 > > z <<- 1 > > cat(sprintf('bar1: x=%d, y=%d, z=%d\n', x, y, z)) > > } > > > > bar2 <- function(){ > > x <<- 2 > > y <<- 2 > > z <<- 2 > > cat(sprintf('bar2: x=%d, y=%d, z=%d\n', x, y, z)) > > } > > > > # some setup code > > dummy <- a + b > > x <- y <- z <- 0 > > > > # here is my scope problem > > if (c==1) bar1() > > if (c==2) bar2() > > > > # some more code > > cat(sprintf('foo: x=%d, y=%d, z=%d\n', x, y, z)) > > > > } > > > > foo(c=0) > > foo(c=1) > > foo(c=2) > > > > I get this output: > > > > > foo(c=0) > > foo: x=0, y=0, z=0 > > > foo(c=1) > > bar1: x=1, y=1, z=1 > > foo: x=1, y=1, z=1 > > > foo(c=2) > > bar2: x=2, y=2, z=2 > > foo: x=2, y=2, z=2 > > > > Duncan Murdoch > > > > ______________________________________________ > R-help@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. > [[alternative HTML version deleted]] ______________________________________________ R-help@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.