Reputation: 1236
I first defined new variable x
, then created function that require x
within its body (not as argument). See code below
x <- c(1,2,3)
f1 <- function() {
x^2
}
rm(x)
f2 <- function() {
x <- c(1,2,3)
f1()
}
f(2)
Error in f1() : object 'x' not found
When I removed x
, and defined new function f2
that first define x
and then execute f1
, it shows objects x
not found.
I just wanted to know why this is not working and how I can overcome this problem. I do not want x
to be name as argument in f1
.
Please provide appropriate title because I do not know what kind of problem is this.
Upvotes: 2
Views: 164
Reputation: 8750
In short: Your are expecting dynamic scoping but are a victim of R's lexical scoping:
dynamic scoping = the enclosing environment of a command is determined during run-time
lexical scoping = the enclosing environment of a command is determined at "compile time"
To understand the lookup path of your variable x
in the current and parent environments try this code.
It shows that both functions do not share the environment in with x
is defined in f2
so it can't never be found:
# list all parent environments of an environment to show the "search path"
parents <- function(env) {
while (TRUE) {
name <- environmentName(env)
txt <- if (nzchar(name)) name else format(env)
cat(txt, "\n")
if (txt == "R_EmptyEnv") break
env <- parent.env(env)
}
}
x <- c(1,2,3)
f1 <- function() {
print("f1:")
parents(environment())
x^2
}
f1() # works
# [1] "f1:"
# <environment: 0x4ebb8b8>
# R_GlobalEnv
# ...
rm(x)
f2 <- function() {
print("f2:")
parents(environment())
x <- c(1,2,3)
f1()
}
f2() # does not find "x"
# [1] "f2:"
# <environment: 0x47b2d18>
# R_GlobalEnv
# ...
# [1] "f1:"
# <environment: 0x4765828>
# R_GlobalEnv
# ...
Possible solutions:
Declare x
in the global environment (bad programming style due to lack of encapsulation)
Use function parameters (this is what functions are made for)
Use a closure if x
has always the same value for each call of f1
(not for beginners). See the other answer from @JohnColeman...
I strongly propose using 2. (add x as parameter - why do you want to avoid this?).
Upvotes: 4
Reputation: 51988
You could use a closure to make an f1
with the desired properties:
makeF <- function(){
x <- c(1,2,3)
f1 <- function() {
x^2
}
f1
}
f1 <- makeF()
f1() #returns 1 4 9
There is no x
in the global scope but f1
still knows about the x
in the environment that it was defined in.
Upvotes: 6