3

I'm having trouble understanding how to bind values from within a function to an environment outside of the function. A basic example is shown below, I cannot seem to solve the last part of the function:

number <- data.frame()

how_many_fruits <- function() {

    answer <- as.integer(readline(prompt="How Many?: "))
    globalenv()$number <- rbind(globalenv()$number, answer)

}

Essentially, I want to have an empty data frame called number in the beginning, and every time how_many_fruits() is run, I want the input to be attached to the bottom of the number data frame.

Steven Beaupré
  • 21,343
  • 7
  • 57
  • 77
jim mako
  • 541
  • 2
  • 9
  • 28

2 Answers2

7

You can use the <<- operator:

number <- data.frame()
how_many_fruits <- function() {

  answer <- as.integer(readline(prompt="How Many?: "))  
  number <<- rbind(number, answer)

}

However, I wonder what your goal is with that procedure. A function should not use variables in the global environment in the way you do. What will happen, if someone wants to use that function, but calls the variable num instead of number? The function won't work in that situation. So I would suggest that you do the following instead:

how_many_fruits <- function(num) {

  answer <- as.integer(readline(prompt="How Many?: "))
  new_num <- rbind(num, answer)

  return (new_num)
}

number <- data.frame()
number <- how_many_fruits(number)

This is how a function is supposed to work: it takes inputs (here called num) and it returns an output (here called new_num). Note how the name of the input and output inside the function need not be the same as the name of the variables you use outside the function. When you call how_many_fruits(number), the contents of number are stored in num and the function only works with the latter. And when you do number <- how_many_fruits(number), whatever is returned as the result of how_many_fruits(number) is stored in number.

Stibu
  • 15,166
  • 6
  • 57
  • 71
3

It's not a good idea to modify the global environment directly from within a function. Usually it is a better idea just to return a value and let the user append it to where's necessary. (Just as Stibu explained).

However, what you can also do is to use nested environments like in the following modification of an example from the official R language definition:

fruitscollector <- function(){
  fruitslist <- NULL
  function(){
    answer <- as.integer(readline(prompt="How Many?: "))
    fruitslist <<- c(fruitslist, answer)
    fruitslist
  }
}

So when you first initialize a "fruitscollector" it returns just a function that can collect values.

foo <- fruitscollector()

Now every time you use foo, a value will be added to the collection (and the whole collection is returned):

foo()
foo()
# etc

fruitslist is stored in the parent environment of foo, so not in the global environment where you can accidentally delete it.

edit

A more general idea would be to create an object (somewhat similar to what is called an "object" in OOP) with functions as methods, e.g.

collector <- function(){
  stack <- NULL
  list(
    add = function(x) stack<<-c(stack, x),
    get = function() stack,
    empty = function() stack <<- NULL
  )
}

Now the add method will add to the stack, get method will return the whole stack, and empty method will empty it.

foo <- collector()  # initialize
foo$get()  # NULL
foo$add(1)  # add 1 to the stack
foo$get()  # 1
foo$add(3)  # add 3 to the stack
foo$get()  # 1 3
foo$add(1:5) # etc...
lebatsnok
  • 6,329
  • 2
  • 21
  • 22