Can somebody please explain what happens when an expression is evaluated in system.time
? In particular, why are any variables that are declared in the expr
argument visible in the global environment?
Here is a pared-down version of the innards of system.time
that does nothing other than evaluating the expression that is passed to the function:
st <- function(expr){
expr
}
st(aa <- 1)
aa
[1] 1
Clearly the effect of this is that it creates the variable aa
in the global environment. This baffles me, since I thought that assigning a variable inside a function makes it local in scope.
What happens here?
Functions can access global variables and modify them. Modifying global variables in a function is considered poor programming practice.
Accessing Specific Variables in an Object Accessing a particular variable (i.e., column) in a data object is simple: DataObject$VarName , where DataObject is the data object and VarName the variable desired. The $ (dollar) symbol is how R links the requested variable to the data object.
It is because supplied arguments are evaluated in the evaluation frame of the calling function (as described in Section 4.3.3 of the R Language Definition document).
The expression wrapped by the user in system.time()
is a supplied argument which gets matched positionally to expr
. Then, when expr
's evaluation is forced in the body of system.time
, it is evaluated in the evaluation frame of the calling function. If system.time()
was called from the .GlobalEnv
, that is where any assignments that are a part of expr
will take place.
EDIT:
Here's an example showing that expr
is evaluated in the global environment if it is a supplied (but not a default) argument.
st2 <- function(expr = newVar <- 33){
expr
}
# Using the default argument -- eval and assignment
# within evaluation frame of the function.
st2()
newVar
Error: object 'newVar' not found
# Using a supplied argument -- eval and assignment
# within the calling function's evaluation frame (here .GlobalEnv)
st2(newVar <- 44)
newVar
# [1] 44
EDIT : as per @Tommy's comment: The evaluation actually only takes place once the argument expr is used (that's the lazy evaluation).
What is passed is a language object, not an expression. You basically nest the <-
function (with two arguments) within the st() function call, and the result of the <-
call is passed to to st. As you can see in ?assignOps
, the <-
function returns the assigned value silently. As @Josh told you already, this evaluation of the nested function takes place in the environment the function is called from.
What you do, is equivalent to
st(mean(1:10))
To see the difference, you can do:
st <- function(expr){
typeof(expr)
}
> st(aa <- 1)
[1] "double"
> st(expression(aa <- 1))
[1] "expression"
For the structure of the call, you can do:
st <- function(expr){
str(as.list(match.call()))
}
> st(mean(1:10))
List of 2
$ : symbol st
$ expr: language mean(1:10)
> st(aa <- 1)
List of 2
$ : symbol st
$ expr: language aa <- 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With