1

I created the following function to append new strings on a vector of strings called "meals". However, when I use this function to append an string input into my "meals" vector, it does not work.

 add <- function(str) {
      meals <- append(meals, as.character(str))  
    }
John Zach
  • 35
  • 1
  • 4
  • 1
    Both of these comments miss the point. The function will work but it needs to be applied in a functional manner, i.e.: `meals <- add( c("lunch", "dinner") )` – IRTFM Sep 30 '16 at 16:52
  • 2
    Because it would return the value of the `append` call. Try it and read `?'<-'` – IRTFM Sep 30 '16 at 17:00
  • I question putting this question on hold. It was not a typo. It was a failure to understand how R functions return values as was exhibited by at least 2 commenters (who have now deleted their comments) and also exhibited by the two answers who also missed this aspect. It could serve as a useful example, but now the closure appears to be sweeping this under the rug. Just because the questioner didn't understand this does not make it a trivial question. It is in fact a rather deep question whose correct answer demonstrates a fundamental difference between R and most other computer languages. – IRTFM Sep 30 '16 at 17:37

3 Answers3

3

The issue here is variable scoping. The variable meals exists in your global environment where you are using it. When you run your function, there is no meals variable inside the function environment, so it inherits that variable from the global environment. However, when it modifies the variable, it creates a new version inside the function environment. The function can then use that modified version, but it never changes the meals variable from the global environment.

So, you can use the function as it is currently written, and just save the result to meals like so:

meals <- add("Steak")

The value is returned invisibly when you save it to a variable at the end of your function, but it does still output (like the full result of lm or hist that are only accessible if you save them to a new variable). This is one reason why it is a good idea to get in the habit of always ending functions with an explicit return or invisible statement.

My guess is that you are coming from a different programming language where changing a global variable inside a function changes the variable globally. That behavior by explicitly telling R to override that environmental behavior with <<-, which assigns the value outside of the current environment (instead of just within it). For example, this:

add <- function(str) {
  meals <<- append(meals, as.character(str))  
}

meals <- "Steak"

add("Chicken")

meals

results in meals holding both "Steak" and "Chicken" -- the behavior you want. HOWEVER, this is generally a bad idea in R. There are times when this behavior is nice, but the fact that it is very different from how R generally operates leaves a lot of opportunity for mistakes to slip in. I use this occasionally to get past scoping issues (often when working in shiny and needing to change things for the whole session, rather than just the current user), but I try to avoid it if at all possible.

I would encourage you to, instead, just use the construction:

meals <- append(meals, str)

directly, rather than trying to wrap this in a custom function that works so differently than standard R approaches.

Mark Peterson
  • 9,370
  • 2
  • 25
  • 48
1

You need to make sure you are assigning the function to output to a variable in your case try something like these (3 possible solutions):

## longest/redundant
add <- function(str) {
   meals <- append(meals, as.character(str))
   return(meals)
}

## alternative
add2 <- function(str) {
   meals <- append(meals, as.character(str))
}

## simplest
add3 <- function(str) {
   append(meals, as.character(str))
}

meals <- c("chx","beef","pork")
str1 <- c(1, 2, 3)

newVar <- add(str1)
print(newVar)
[1] "chx"  "beef" "pork" "1"    "2"    "3"  

newVar2 <- add2(str1)
print(newVar2)
[1] "chx"  "beef" "pork" "1"    "2"    "3"  

newVar3 <- add3(str1)
print(newVar3)
[1] "chx"  "beef" "pork" "1"    "2"    "3"  

You can also consider an easier solution:

c(meals, str1) which will return the same exact thing. append is typically only used if you need to use the after argument.

TBSRounder
  • 348
  • 1
  • 9
  • Using return is unnecessary on this case. And this answer prints that value but leaves the user that str has been changed. Check it. It remains unchanged. – IRTFM Sep 30 '16 at 17:05
  • [Interesting discussion on whether to exclusively use return() or not](http://stackoverflow.com/questions/11738823/explicitly-calling-return-in-a-function-or-not) – TBSRounder Sep 30 '16 at 17:20
  • The addition is good but you still should point out that the return() call was not needed, and in fact the `meals <-` was not needed either. – IRTFM Sep 30 '16 at 17:32
1

How does it not work?

One thing to consider: The output of a function is the last line in the function. for example:

x.plus.3 <- function(x){
        s <- x+3
}
x.plus.3(2)
#  nothing is printed

This is a valid function but if ran will have no output because all the function does is store x+3 into the object s.

If however, I change it to :

 x.plus.3 <- function(x){
         s <- x+3
         s
 }
 x.plus.3(2)
[1] 5  # i get the result printed

So you need to add meals as the last line in your function in order to get it to make it outside the function.

Puddlebunk
  • 493
  • 3
  • 10