[R] Question about function scope
Neal Fultz
n|u|tz @end|ng |rom gm@||@com
Wed Oct 31 02:28:53 CET 2018
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.duncan using 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.duncan using gmail.com>
> > To: "Sebastien Bihorel" <sebastien.bihorel using cognigencorp.com>,
> r-help using 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 using 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]]
More information about the R-help
mailing list