问题
I defined a function:
.get <- function( o, ...) {
p <- match.call( expand.dots = 0)$...
cat( sprintf( 'In .get, it is %s.\n', eval( tail( p, 1)[[ 1]])))
fn <- switch( typeof( o), list =, environment = `[[`, 'S4' = '@', `[`)
if( length( p)) eval( as.call( c( fn, quote( o), p))) else o # Here when true, I compose a call based on p.
}
Then I tried it as follows:
it <- 1
m <- matrix( seq( 9), 3)
sapply( seq( 3), function( it) {
cat( sprintf( 'In sapply, it is: %s.\n', it))
.get( m, , it)
})
sapply( seq( 3), function( it) .get( m, , it))
The output:
In sapply, it is: 1.
In .get, it is 1.
In sapply, it is: 2.
In .get, it is 1.
In sapply, it is: 3.
In .get, it is 1.
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 2 2 2
[3,] 3 3 3
But the expected output is:
In sapply, it is: 1.
In .get, it is 1.
In sapply, it is: 2.
In .get, it is 2.
In sapply, it is: 3.
In .get, it is 3.
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
So why is it
not 1 to 3 (the value it has where the function was called), but always the value assigned in the global environment (i.e. 1)?
回答1:
Did you define get
in the global environment together with it
? If so, then this might be a scoping-issue. See here and here for an excellent discussion.
Look at this example from the first link:
a = 1
b = 2
fun <- function(x){ a + b*x }
new.fun <- function(x){
a = 2
b = 1
fun(x)
}
new.fun(2)
If fun
called within new.fun
uses a
and b
from the global environment, we expect the outcome of new.fun(2)
to be 1+2*2=5
whereas if it using the parameters defined in the function new.fun
, then it should be 2+1*2=4
. Now, most people expect the outcome to be 4, but it will be 5. Why? Because fun
was defined in the global environment, and hence the global variables a
and b
matter for fun
. To see this, you can look at the structure of the function with str(fun)
which will reveal that an environment is attached to the function. Looking into that environment with list(environment(fun))
, you will see that the function "remembers" that it was defined in the global environment. For that reason, the function fun
will look there first to find the parameters a
and b
.
To adress the isssue, many workarounds have been proposed, several of which can be found if you google lexical scoping. For background information, Hadley Wickam's upcoming book has an excellent section on environments, see here. For potential solutions, see, for instance here. One way to solve your issue is to overwrite the environment. For instance,
new.fun2 <- function(x){
a = 2
b = 1
environment(fun) = environment()
fun(x)
}
new.fun2(2)
now gives 4 as the answer, using a=2, b=1
as defined in the parent environment, as opposed to the global environment. I am sure there are many more elegant solutions though.
That is, in your case, using
sapply( seq( 3), function(it) {
cat( sprintf( 'In sapply, it is: %s.\n', it))
environment(.get) <- environment()
.get( m, , it)
})
works.
回答2:
Another solution is using constructors:
make.get <- function(it){
it <- it
.get <- function( o, ...) {
p <- match.call( expand.dots = 0)$...
cat( sprintf( 'In .get, it is %s.\n', eval( tail( p, 1)[[ 1]])))
fn <- switch( typeof( o), list =, environment = `[[`, 'S4' = '@', `[`)
if( length( p)) eval( as.call( c( fn, quote( o), p))) else o # Here when true, I compose a call based on p.
}
}
it <- 1
m <- matrix( seq( 9), 3)
sapply( seq( 3), function(it) {
cat( sprintf( 'In sapply, it is: %s.\n', it))
.get <- make.get(it)
.get( m, , it)
})
回答3:
Languages with lexical scope have functions with only passed parameters and global variables. Languages with dynamic scope have functions which use the environment of the caller. Lexical scoping has won because it is easier to reason about (and it stateless languages like Haskell, it even provides referential transparency). There are still some dynamically scoped languages, like bash and (optionally) common lisp. Its interesting that you expected dynamic scoping as a default; at some point I had the same expectation.
来源:https://stackoverflow.com/questions/23234640/why-do-variable-lookups-in-the-body-of-function-a-take-values-from-the-global-en