Neeraj
Neeraj

Reputation: 1236

Why is this simple function not working?

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

Answers (2)

R Yoda
R Yoda

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:

  1. Declare x in the global environment (bad programming style due to lack of encapsulation)

  2. Use function parameters (this is what functions are made for)

  3. 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

John Coleman
John Coleman

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

Related Questions