Use the superassignment operator

One of the most important functional programming principle is that functions do not change non-local variables; that is, generally speaking, the code in a function only has read access to its non-local variables.  This is a quite important feature which can protect the higher-level variable from being changed by local functions. See the example below.

> x <- 10
> test <- function(x) {
+   x <- x - 5
+   print(x)
+ }
> test(x)
[1] 5
> x
[1] 10

However, sometimes you may wish to write to a global variable or any variable higher than the level at which your write statement exists. The superassignment operator, <<-, or the assign() function is what you want. Let’s look at the superassignment operator first.

> test <- function(x) {
+   # The only difference here is using <<- instead of <-
+   x <<- x - 5
+   y <<- x - 5
+ }
> x <- 10
> x
[1] 10
> y
Error: object 'y' not found
> test(x)
> x
[1] 5
> y
[1] 5

See, <<- change the value of top-level variable x and also create a top-level variable y when we call the test() function.

It’s worth noting that although <<- is typically used to write to top-level variables, technically it does not always write to global variable. Use of <<- operator will result in a search up the environment hierarchy, stopping at the first level at which a variable of that name is encountered. If none of found, then the level selected will be global.

> x <- 5
> test <- function() {
+   x <- 10
+   double <- function() {x <<- 2*x}
+   double()
+   print(x)
+ }
> test()
[1] 20
> x
[1] 5

The double() function was defined within the test() function. So when double() is running, the R interpreter see the <<- and starts going up the hierarchy. And it finds an x at the first level up – the environment within test(). Therefore, the x in test() is the one that is written to, not the global x.

Now let’s look at the assign() function. Instead of writing to the x in test(), the global x is written because I set the pos argument equal to global environment. And the fact that your reference variables using character strings in assign() can come in handy. In fact, the assign() function is a kinda combination of <<- and get() function if you still remember the get() function introduced in the last post.

> test <- function() {
+   x <- 10
+   double <- function() {assign("x", 2*x, pos=.GlobalEnv)}
+   double()
+   print(x)
+ }
> test()
[1] 10
> x 
[1] 20

And last, try not to use the tools mentioned above to overwrite the non-local variable, even if it is really convenient, be careful until you 100% know what you’re doing.

 

Reference: The Art of R Programming by Norman Matloff

Posted in R Programming Tips and tagged .

Leave a Reply

Your email address will not be published. Required fields are marked *