Reputation: 178
After some hours I found a bug in my code because an unexpected behaviour of the <<-
assinament operator in r. I have read the documentation and browsed on internet but I still do not understand the behaviour of the operator.
Look these two functions:
# Define a function a_counter
a_counter <- function(){
i <<- i + 1
print(i)
}
> i <- 0
> a_counter()
[1] 1
> print(i)
[1] 1
> a_counter()
[1] 2
> print(i)
[1] 2
# Define a function not_a_counter
not_a_counter <- function(){
i <- 0
i <<- i + 1
print(i)
}
> i <- 0
> not_a_counter()
[1] 0
> print(i)
[1] 1
> not_a_counter()
[1] 0
> print(i)
[1] 1
The first chunk of code runs as I expected, the variable i
in both environments (function env. and global env.) are increased in each function call.
The second chunk of code is absolutely unexpected for me. The i <<- i + 1
does not assign the value to the i
located in the function environment, but it does so in the i
located in the global environment. I expected both enviroments to be updated.
Upvotes: 0
Views: 69
Reputation: 206566
In your a_counter
, there is only one value of i
. Not two. When a "free variable" is found in a function, it's looked up in the environment in which the function was defined. So when you call i
in that function, it goes up to the global environment to find the value. Then when you do <<-
, assignment doesn't happen in the function environment at all. <<-
always starts looking in the parent environment. If you look at
counter_vars <- function() {
a <- 4
i <<- i + 1
ls()
}
counter_vars()
# [1] "a"
you'll see that the only variable inside the function environment/closure is the a
variable. The i
variable doesn't exist there. So with the original function all the i
s come from global scope
a_counter <- function(){
i <<- i + 1 # global i = global i + 1
print(i) # global i still (no local variable has been created)
}
So the behavior you see in not_a_counter
should be expected because <<-
will not change values in current environment. It always start looking one environment "up". When you have
not_a_counter <- function(){
i <- 0 # local i (always 0)
i <<- i + 1 # global i == local i + 1 (always 0+1)
print(i) # local i (always still 0, local value not changed)
}
The i
variable is no longer "free" once you define it in the function. So i <- 0
creates a local variable, in the i <<- i + 1
part, the right hand side i+1
uses that local variable and assigns to the i
in the parent environment.
Upvotes: 4