seaFan
seaFan

Reputation: 116

Why does using a variable in a function change the value returned in R when using superassignment (<<-) operator?

Why do bar and baz behave differently? When bar is called both the value of a printed and the value of a in the global scope are the same, but when baz is called the printed value and the value in the global scope are different. Seemingly, the only difference is that a is used (but not defined) in a parent environment.

a = 1:3
b = 4
foo <- function(a) {
  a[1] <<- b
  print(a)
}

bar <- function(a) {
  foo(a)
}

baz <- function(a) {
  a
  foo(a)
}

bar(a) # 4 2 3
a # 4 2 3 

a <- 1:3
baz(a) # 1 2 3
a # 1 2 3

Upvotes: 3

Views: 57

Answers (1)

Anoushiravan R
Anoushiravan R

Reputation: 21918

Complex assignment operator <<- changes the value of a variable in the parent environment. When bar is called:

  • It passes a to foo
  • foo then changes the value of first element of a to 4 in global environment
  • After that foo prints a
> bar(a) # 4 2 3
[1] 4 2 3

The only thing to note here is, since foo was created in global environment, it searches for the value of b through lexical scoping in the environment where it was created which is again global environment. Such is the case when foo prints a in the end, it again searches for its value in the environment where it was created which is global environment. So the a will change to c(4, 2, 3).

However, when you call baz,

  • It first prints a which is the original c(1, 2, 3)
  • Then it passes it to foo where the same thing that I explained above happens

So that's why the first print is the original a and the second is the modified one.

A resume of our discussion in comments:

  • The first line of code in baz (first one) is a which is equivalent to print(a). In your second baz the output is the same cause the output for the first line of code is not returned. No matter if you use mean or sum. If you want the output you should use print() and force it to return. In that case the output of foo(a) won't return. But if you want both you can just use a list for example
  • The other reason that the output is still 1:3 has got nothing to do with mean , it is because of foo. Cause it changes the first value of object a in the global environment. But then it prints the value of argument a. There is difference here between free variable a that foo finds through lexical scoping in global env and argument a which you assign yourself to baz to be then passed to foo
  • Just note that using a in the first baz is like using print(a) so whatever is printed is the result of what baz does not foo contrary to what I explain above
  • You can use rm(a) to remove a from global environment and use baz <- function(a) { foo(a) } and baz(c(1,2,3) to better differentiate between the object a and argument a

Upvotes: 3

Related Questions