2

So, I'm brushing up on how to work with data frames in R and I came across this little bit of code from https://cloud.r-project.org/web/packages/data.table/vignettes/datatable-intro.html:

input <- if (file.exists("flights14.csv")) {
   "flights14.csv"
} else {
  "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
}

Apparently, this assigns the strings (character vectors?) in the if and else statements to input based on the conditional. How is this working? It seems like magic. I am hoping to find somewhere in the official R documentation that explains this.

From other languages I would have just done:

if (file.exists("flights14.csv")) {
   input <- "flights14.csv"
} else {
  input <- "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
}

or in R there is ifelse which also seems designed to do exactly this, but somehow that first example also works. I can memorize that this works but I'm wondering if I'm missing the opportunity to understand the bigger picture about how R works.

IRTFM
  • 258,963
  • 21
  • 364
  • 487
Jimbo
  • 2,886
  • 2
  • 29
  • 45

3 Answers3

3

From the documentation on the ?Control help page under "Value"

if returns the value of the expression evaluated, or NULL invisibly if none was (which may happen if there is no else).

So the if statement is kind of like a function that returns a value. The value that's returned is the result of either evaulating the if or the then block. When you have a block in R (code between {}), the brackets are also like a function that just return the value of the last expression evaluated in the block. And a string literal is a valid expression that returns itself

So these are the same

x <- "hello"
x <- {"hello"}
x <- {"dropped"; "hello"}
x <- if(TRUE) {"hello"}
x <- if(TRUE) {"dropped"; "hello"}
x <- if(TRUE) {"hello"} else {"dropped"}

And you only really need blocks {} with if/else statements when you have more than one expression to run or when spanning multiple lines. So you could also do

x <- if(TRUE) "hello" else "dropped"
x <- if(FALSE) "dropped" else "hello"

These all store "hello" in x

MrFlick
  • 195,160
  • 17
  • 277
  • 295
2

You are not really missing anything about the "big picture" in R. The R if function is atypical compared both to other languages as well as to R's typical behavior. Unlike most functions in R which do require assignment of their output to a "symbol", i.e a proper R name, if allows assignments that occur within its consequent or alternative code blocks to occur within the global environment. Most functions would return only the final evaluation, while anything else that occurred inside the function body would be garbage collected.

The other common atypical function is for. R for-loops only retain these interior assignments and always return NULL. The R Language Definition calls these atypical R functions "control structures". See section 3.3. On my machine (and I suspect most Linux boxes) that document is installed at: http://127.0.0.1:10731/help/doc/manual/R-lang.html#Control-structures. If you are on another OS then there is probably a pulldown Help menu in your IDE that will have a pointer to it. Thew help document calls them "control flow constructs" and the help page is at ?Control. Note that it is necessary to quote these terms when you wnat to access that help page using one of those names since they are "reserved words". So you would need ?'if' rather than typing ?if. The other reserved words are described in the ?Reserved page.

?Control
?'if'  ; ?'for'
?Reserved

# When you just type:
?if  # and hit <return>
# you will see a "+"-sign which indicateds an incomplete expression.
# you nthen need to hit <escape> to get back to a regular R interaction. 
IRTFM
  • 258,963
  • 21
  • 364
  • 487
1

In R, functions don't need explicit return. If not specified the last line of the function is automatically returned. Consider this example :

a <- 5
b <- 1

result <- if(a == 5) {
  a <- a + 1
  b <- b + 1
  a
} else {b}
result
#[1] 6

The last line in if block was saved in result. Similarly, in your case the string values are "returned" implicitly.

Ronak Shah
  • 377,200
  • 20
  • 156
  • 213