---
title: "Function Basics"
author: "Statistical Computing, 36-350"
date: "Monday September 26, 2016"
---

Why do we need functions?
===

- Data structures tie related values into one object
- Functions tie related commands into one object
- In both cases: easier to understand, easier to work with, easier to build into larger things

Remember those commands you typed over and over?
===

From our lectures on text manipulation and regexes:

```{r}
# Get Trump's word counts
trump.lines = readLines("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt")
trump.text = paste(trump.lines, collapse=" ")
trump.words = strsplit(trump.text, split="[[:space:]]|[[:punct:]]")[[1]]
trump.words = trump.words[trump.words != ""]
trump.wordtab = table(trump.words)

# Now do the same for Clinton, Pence, Kaine, etc...
```

Creating your own function
===

Call `function()` to create your own function. Optional (but highly recommended): document your function with comments

```{r}
# get.wordtab: get a word table from text on the web
# Inputs:
# - str.url: string, specifying URL of a web page 
# Output: word table, i.e., vector with counts as entries and associated
#   words as names

get.wordtab = function(str.url) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split="[[:space:]]|[[:punct:]]")[[1]]
  words = words[words != ""]
  wordtab = table(words)
  return(wordtab)
}
```

Some notes
===

The structure of a function has three basic parts:  

- **Inputs** (or **arguments**)  
- **Body** (code that is executed)
- **Output** (or **return value**)  

R doesn't let your function have multiple outputs, but you can return a list

Functions can call other functions:  

- Either built-in R functions (look at what we did above)  
- Or other functions you've created (helpful for design)  

Using your created function
===

Our created functions can be used just like the built-in ones

```{r}
# Using our function
trump.wordtab.new = get.wordtab("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt")
all(trump.wordtab.new == trump.wordtab)

# Revealing our function's definition
get.wordtab
```

Default return value
===

With no explicit `return()` statement, the default is just to return whatever is on the last line. So the following is exactly the same as before

```{r}
get.wordtab = function(str.url) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split="[[:space:]]|[[:punct:]]")[[1]]
  words = words[words != ""]
  table(words)
}
```

Multiple inputs
===

Our function can take more than one input

```{r}
# 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
# Output: word table, i.e., vector with counts as entries and associated
#   words as names

get.wordtab = function(str.url, split) {
  lines = readLines(str.url)
  text = paste(lines, collapse=" ")
  words = strsplit(text, split=split)[[1]]
  words = words[words != ""]
  table(words)
}
```

Default inputs
===

Our function can also specify default values for the inputs (so if the user doesn't specify the input in the function call, then the default value is used)

```{r}
# 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
# Output: word table, i.e., vector with counts as entries and associated
#   words as names

get.wordtab = function(str.url, split="[[:space:]]|[[:punct:]]",
                       tolower=TRUE) {
  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)
  
  table(words)
}
```

Examples of function calls
===

```{r}
# Inputs can be called by name, or without names
trump.word.tab.1 = get.wordtab(
  str.url="http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  split="[[:space:]]|[[:punct:]]", tolower=TRUE)
trump.word.tab.2 = get.wordtab(
  "http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  "[[:space:]]|[[:punct:]]", TRUE)
all(trump.word.tab.2 == trump.word.tab.1)

# Inputs can be called by partial names (if uniquely identifying)
trump.word.tab.3 = get.wordtab(
  str="http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  spl="[[:space:]]|[[:punct:]]", tolower=TRUE)
all(trump.word.tab.3 == trump.word.tab.1)

# When inputs aren't specified, default values are used
trump.word.tab.4 = get.wordtab(
  str.url="http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  split="[[:space:]]|[[:punct:]]")
all(trump.word.tab.4 == trump.word.tab.1)

# Named inputs can go in any order
trump.word.tab.5 = get.wordtab(tolower=TRUE, split="[[:space:]]|[[:punct:]]",
  str.url="http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt")
all(trump.word.tab.5 == trump.word.tab.1)
```

The dangers of using inputs without names
===

While named inputs can go in any order, unnamed inputs must go in the proper order (as they are specified in the function's definition)

So the following code would throw an error:

```{r, error=TRUE}
trump.word.tab.6 = get.wordtab("[[:space:]]|[[:punct:]]",
  "http://www.stat.cmu.edu/~ryantibs/statcomp-F16/data/trump.txt",
  tolower=FALSE)
```

because our function would try to open up "[[:space:]]|[[:punct:]]" as the URL of a web page

When calling a function with multiple arguments, **use input names** for safety, unless you're absolutely certain of the right order for (some) inputs