Returning more than one thing

When creating a function in R, though you cannot return more than one output, you can return a list. This (by definition) can contain an arbitrary number of arbitrary objects

# Define some functions
identity.1 = function(x, y) { list(x, y) }
identity.2 = function(x, y) { list(x=x, y=y) }
identity.3 = function(x, y) { return(x, y) }

# Now let's run them
identity.1(1, 2)
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
identity.2(1, 2) # Notice the names (helpful)
## $x
## [1] 1
## 
## $y
## [1] 2
identity.2(1, "2") # Notice the mixed data types
## $x
## [1] 1
## 
## $y
## [1] "2"
identity.2(1, 2:8) # Again, the mixed data types
## $x
## [1] 1
## 
## $y
## [1] 2 3 4 5 6 7 8

Running the following code would produce an error:

identity.3(1, 2)
## Error in return(x, y): multi-argument returns are not permitted

Another example with a returned list

# get.wordtab: get a word table from text on the web
# Inputs:
# - str.url: string, specifying URL of a web page 
# - split: string, specifying what to split on. Default is the regex pattern
#   "[[:space:]]|[[:punct:]]"
# - tolower: boolean, TRUE if words should be converted to lower case before
#   the word table is computed. Default is TRUE
# - keep.numbers: boolean, TRUE if words containing numbers should be kept in
#   the word table. Default is FALSE
# Output: list, containing word table, and then some basic numeric summaries

get.wordtab = function(str.url, split="[[:space:]]|[[:punct:]]",
                       tolower=TRUE, keep.numbers=FALSE) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split=split)[[1]]
  words = words[words != ""]
    
  # Convert to lower case, if we're asked to
  if (tolower) words = tolower(words)
  
  # Get rid of words with numbers, if we're asked to
  if (!keep.numbers)
    words = grep("[0-9]", words, inv=TRUE, val=TRUE)
    
  # Compute the word table
  wordtab = table(words)
  
  return(list(wordtab=wordtab,
              number.unique.words=length(wordtab),
              number.total.words=sum(wordtab)))
}

(Continued)

# Trump's speech
trump.wordtab = get.wordtab("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt")
lapply(trump.wordtab, head)
## $wordtab
## words
##         a   abandon abandoned      able   abolish     about 
##        54         1         1         2         1         4 
## 
## $number.unique.words
## [1] 1236
## 
## $number.total.words
## [1] 4431
# Clinton's speech
clinton.wordtab = get.wordtab("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/clinton.txt")
lapply(clinton.wordtab, head)
## $wordtab
## words
##         a abandoned      able     about    abroad    accept 
##       106         1         1        11         1         1 
## 
## $number.unique.words
## [1] 1306
## 
## $number.total.words
## [1] 5555

Side effects

A side effect of a function is something that happens as a result of the function’s body, but is not returned

Examples:

Example of a plotting side effect

# get.wordtab: get a word table from text on the web
# Inputs:
# - str.url: string, specifying URL of a web page 
# - split: string, specifying what to split on. Default is the regex pattern
#   "[[:space:]]|[[:punct:]]"
# - tolower: boolean, TRUE if words should be converted to lower case before
#   the word table is computed. Default is TRUE
# - keep.numbers: boolean, TRUE if words containing numbers should be kept in
#   the word table. Default is FALSE
# - plot.wordtab: boolean, TRUE if word table should be plotted as a side 
#   effect. Default is FALSE
# Output: list, containing word table, and then some basic numeric summaries

get.wordtab = function(str.url, split="[[:space:]]|[[:punct:]]",
                       tolower=TRUE, keep.numbers=FALSE, plot.wordtab=FALSE) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split=split)[[1]]
  words = words[words != ""]
    
  # Convert to lower case, if we're asked to
  if (tolower) words = tolower(words)
  
  # Get rid of words with numbers, if we're asked to
  if (!keep.numbers)
    words = grep("[0-9]", words, inv=TRUE, val=TRUE)
  
  # Compute the word table
  wordtab = table(words)
  
  # Plot the word table, if we're asked to
  if (plot.wordtab) plot(wordtab)
  
  return(list(wordtab=wordtab,
              number.unique.words=length(wordtab),
              number.total.words=sum(wordtab)))
}

(Continued)

# Trump's speech
trump.wordtab = get.wordtab(
  "http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  plot=TRUE)

lapply(trump.wordtab, head)
## $wordtab
## words
##         a   abandon abandoned      able   abolish     about 
##        54         1         1         2         1         4 
## 
## $number.unique.words
## [1] 1236
## 
## $number.total.words
## [1] 4431

Bad side effects

Not all side effects are desirable. One particularly bad side effect is if the function’s body changes the value of some variable defined outside of the function

Not easy to do, but can be done and should be avoided! (We’ll discuss function enviroments next time)