Question about function scope

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Question about function scope

Sebastien Bihorel
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. ")
- In my real example, I have a few dozens of variables to transform in bar1 and bar2 so exporting a list bundling all the transform values then dispatching them in foo does not seem practical
- currently, my best solution is to save the inner part of bar1 and bar2 in separate scripts and sourcing them in foo (that does not seem elegant...)
- also, I know that, theoretically, I could put the content of bar1 and bar2 directly in foo, but as the number of cases handled by foo grows, the code will become way too long and hardly manageable.

Thanks for your help


######################################

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))
}

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)

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Duncan Murdoch-2
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

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Eric Berger
Hi Sebastien,
I like Duncan's response. An alternative approach is to pass around
environments, as in the following:

bar1 <- function(e) {
  e$x <- e$y <- e$z <- 1
  cat(sprintf('bar1: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}
bar2 <- function(e) {
  e$x <- e$y <- e$z <- 2
  cat(sprintf('bar2: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}

foo <- function(a=1, b=2, c=0, e){
    # some setup code
  dummy <- a + b
  e$x <- e$y <- e$z <- 0

  # here is my scope problem
  if (c==1) bar1(e)
  if (c==2) bar2(e)

  # some more code
  cat(sprintf('foo: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}

e <- new.env()
e$x <- NA
e$y <- NA
e$z <- NA

foo(c=0,e=e)
foo(c=1,e=e)
foo(c=2,e=e)


HTH,
Eric

On Tue, Oct 30, 2018 at 10:13 PM Duncan Murdoch <[hidden email]>
wrote:

> 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
>
> ______________________________________________
> [hidden email] 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]]

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Sebastien Bihorel
In reply to this post by Duncan Murdoch-2
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.


----- Original Message -----
From: "Duncan Murdoch" <[hidden email]>
To: "Sebastien Bihorel" <[hidden email]>, [hidden email]
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

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Duncan Murdoch-2
In reply to this post by Sebastien Bihorel
Here's another modification to your code that also works.  It's a lot
uglier, but will allow bar1 and bar2 to be used in multiple functions,
not just foo.

bar1 <- function(env){
   env$x <- 1
   env$y <- 1
   env$z <- 1
   with(env, cat(sprintf('bar1: x=%d, y=%d, z=%d\n', x, y, z)))
}

bar2 <- function(env){
   env$x <- 2
   env$y <- 2
   env$z <- 2
   with(env, cat(sprintf('bar2: x=%d, y=%d, z=%d\n', x, y, z)))
}

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(environment())
   if (c==2) bar2(environment())

   # 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)

Duncan Murdoch

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Sebastien Bihorel

That's cool! I think this solution would fit better with what my intended setup.

Thanks a lot


----- Original Message -----
From: "Duncan Murdoch" <[hidden email]>
To: "Sebastien Bihorel" <[hidden email]>, [hidden email]
Sent: Tuesday, October 30, 2018 4:18:51 PM
Subject: Re: [R] Question about function scope

Here's another modification to your code that also works.  It's a lot
uglier, but will allow bar1 and bar2 to be used in multiple functions,
not just foo.

bar1 <- function(env){
   env$x <- 1
   env$y <- 1
   env$z <- 1
   with(env, cat(sprintf('bar1: x=%d, y=%d, z=%d\n', x, y, z)))
}

bar2 <- function(env){
   env$x <- 2
   env$y <- 2
   env$z <- 2
   with(env, cat(sprintf('bar2: x=%d, y=%d, z=%d\n', x, y, z)))
}

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(environment())
   if (c==2) bar2(environment())

   # 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)

Duncan Murdoch

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Sebastien Bihorel
In reply to this post by Eric Berger

Thanks a lot Eric,

I think you are on the same page as Duncan (at least with his 2nd option). I will definitively explore this.


From: "Eric Berger" <[hidden email]>
To: "Duncan Murdoch" <[hidden email]>
Cc: "Sebastien Bihorel" <[hidden email]>, "R mailing list" <[hidden email]>
Sent: Tuesday, October 30, 2018 4:17:30 PM
Subject: Re: [R] Question about function scope

Hi Sebastien,
I like Duncan's response. An alternative approach is to pass around environments, as in the following:

bar1 <- function(e) {
e$x <- e$y <- e$z <- 1
cat(sprintf('bar1: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}
bar2 <- function(e) {
e$x <- e$y <- e$z <- 2
cat(sprintf('bar2: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}

foo <- function(a=1, b=2, c=0, e){
# some setup code
dummy <- a + b
e$x <- e$y <- e$z <- 0
# here is my scope problem
if (c==1) bar1(e)
if (c==2) bar2(e)
# some more code
cat(sprintf('foo: x=%d, y=%d, z=%d\n', e$x, e$y, e$z))
}

e <- new.env()
e$x <- NA
e$y <- NA
e$z <- NA

foo(c=0,e=e)
foo(c=1,e=e)
foo(c=2,e=e)


HTH,
Eric

On Tue, Oct 30, 2018 at 10:13 PM Duncan Murdoch < [ mailto:[hidden email] | [hidden email] ] > wrote:


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

______________________________________________
[ mailto:[hidden email] | [hidden email] ] mailing list -- To UNSUBSCRIBE and more, see
[ https://stat.ethz.ch/mailman/listinfo/r-help | https://stat.ethz.ch/mailman/listinfo/r-help ]
PLEASE do read the posting guide [ http://www.r-project.org/posting-guide.html | http://www.R-project.org/posting-guide.html ]
and provide commented, minimal, self-contained, reproducible code.





        [[alternative HTML version deleted]]

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Duncan Murdoch-2
In reply to this post by Sebastien Bihorel
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" <[hidden email]>
> To: "Sebastien Bihorel" <[hidden email]>, [hidden email]
> 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
>

______________________________________________
[hidden email] 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.
Reply | Threaded
Open this post in threaded view
|

Re: Question about function scope

Neal Fultz-2
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 <[hidden email]>
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" <[hidden email]>
> > To: "Sebastien Bihorel" <[hidden email]>,
> [hidden email]
> > 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
> >
>
> ______________________________________________
> [hidden email] 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]]

______________________________________________
[hidden email] 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.