```{r}
rm(list = ls()) # clean-up workspace
```

## R's data structures

|      | Homogeneous | Heterogeneous |
| ---- | ----------- | ------------- |
| 1d   | Atomic vector | List |
| 2d   | Matrix      | Data frame |
| nd   | Array       |            |

 - _Homogeneous_: all contents must be of the same type
 
 - _Heterogeneous_: the contents can be of different types


## Vectors

 - The basic data structure in R.
 
 - Two flavors: _atomic vectors_ and _lists_
 
 - Three common properties:
 
   - Type, `typeof()`, what it is.
   
   - Length, `length()`, how many elements it contains.
   
   - Attributes, `attributes()`, additional arbitrary metadata.
   
 - No scalars in R.  They are length 1 vectors.

**Note**: `is.vector()` does not test if an object is a vector.  Use `is.atomic()` or `is.list()` to test.

### Atomic vectors

 - There are four common types of atomic vectors (remember Lab 2?)
 
   - logical
   
   - integer
   
   - numeric (actually double)
   
   - character



Many commands in R generate a vector of output, rather than a single number.

The `c()` command: creates a vector containing a list of specific elements.

**Example 1**

```{r}
c(7, 3, 6, 0)
c(73:60)
c(7:3, 6:0)
c(rep(7:3, 6), 0)
```

**Example 2**
The command `seq()` creates a sequence of numbers.
```{r}
seq(7)
seq(3, 70, by = 6)
seq(3, 70, length = 6)
```

 - Atomic vectors are **always flat**, even if you nest `c()`'s:
 
**Example 3**
```{r}
c(1, c(2, c(3, 4)))
```

### Lists

 - Elements can be of any type, including lists.
 
 - Construct list by using `list()` instead of `c()`.
 
```{r}
x <- list(1:3, "a", c(TRUE, FALSE, TRUE), c(2.3, 5.9))
str(x)
```

 - Can be named, can access by name with `$`.
 
```{r}
x.named <- list(vector = 1:3, name = "a", logical = c(TRUE, FALSE, TRUE), range = c(2.3, 5.9))
str(x.named)
x.named$vector
x.named$range
```

 - Lists are used to build up many of the more complicated data structures in R.
 
 - For example, both data frames (another data structure in R) and linear models objects (as produced by `lm()`) are lists.
 
### Attributes
 
 - All objects can have arbitrary additional attributes to store metadata about the object.
 
 - Attributes can be thought as a _named list_.
 
 - Use `attr()` to access individual attribute or `attributes()` to access all attributes as a list.
 
 - By default, most attributes are lost when modifying a vector.  Only the most important ones stay:
 
   - Names, a character vector giving each element a name.
  
   - Dimensions, used to turn vectors into matrices and arrays.
  
   - Class, used to implement [S3](http://adv-r.had.co.nz/OO-essentials.html#s3) object system.
 
```{r}
y <- 1:10
attr(y, "my_attribute") <- "This is a vector"
attr(y, "my_attribute")
str(y)
str(attributes(y))
```

### Factors

 - A factor is a vector that can contain only predefined values and is used to store _categorical_ data.
 
 - Built upon integer vectors using two attributes: 
   
   - the `class`, "factor": makes them behave differently from regular integer vectors
   
   - the `levels`: defines the set of allowed values
   
 - Sometimes when a data frame is read directly from a file, you may get a column of factor instead of numeric because of non-numeric value in the column (e.g. missing value encoded specially)
 
   - Possible remedy: coerce the vector from a factor to a character vecctor, and then from a character to a double vector
   
   - Better use `na.strings` argument to `read.csv()` function


### Matrices and arrays

 - adding a `dim` attribute to an atomic vector allows it to behave like a multi-dimensional **array**
 
 - **matrix** is a special case of array

 - `matrix()` command creates a matrix from the given set of values

```{r}
# Two scalar arguments to specify rows and columns
a <- matrix(1:6, ncol = 3, nrow = 2)
# One vector argument to describe all dimensions
b <- array(1:12, c(2, 3, 2))

# You can also modify an object in place by setting dim()
c <- 1:6
dim(c) <- c(3, 2)
c
dim(c) <- c(2, 3)
c
```



**Exercise**
Write a command to generate a random permutation of the numbers between 1 and 5 and save it to an object.

```{r}
set.seed(7360)  # the course seed number
order(runif(5))
sample(1:5, 5)
```


### Data frames

 - Most common way of storing data in R
 
 - A list of equal-length vectors
 
 - 2-dimensional structure, shares properties of both `matrix` and `list`
 
   - has attributes, `names()`, `colnames()` and `rownames()`
   
   - `length()` of a data frame is the length of the underlying list, same as `ncol()`

 - We will focus more on `tibble`, a data frame, but more.

--------------------------------------------------------------------------

## Functions

 - Functions are a fundamental building block of R
 
 - Functions are **objects** in their own right (so that they can have `attributes()`)
 
 - [Functional programming](http://adv-r.had.co.nz/Functional-programming.html#functional-programming)

### Function components

 - All R functions have three parts:

    - the `formals()`, the list of **arguments** which controls how you can call the function
 
    - the `body()`, the **code** inside the function
    
    - the `environment()`, the "map" of the location of the function's variables
    
```{r}
f <- function(x) x^2
f

formals(f)
body(f)
environment(f)
```


## Define a function

 - There is no special syntax for defining and naming a function
 
 - simply create a function object (with `function`) and bind it to a name with `<-`

```{r}
DoNothing <- function() {
  return(invisible(NULL))
}
DoNothing()
```

## Invoke a function

 - You normally call a function by placing its arguments, wrapped in parentheses, after its name:
 
```{r}
mean(1:10, na.rm = TRUE)
```
 
 - What if you have the arguments already in a data structure?
 
```{r}
args <- list(1:10, na.rm = TRUE)
do.call(mean, args)
```
 
 - You can use `do.call()`.
 
## Lexical scoping

 - Now let's discuss **scoping**
 
 - R uses **lexical scoping** that follows four primary rules:
 
   - Name masking
   
   - Functions versus variables
   
   - A fresh start
   
   - Dynamic lookup
   

### Name masking

 - Names defined inside a function **mask** names defined outside a function
 
```{r}
x <- 10
y <- 20

g02 <- function(){
   x <- 1  # a local variable to the function
   y <- 2
   c(x, y)
}
g02()
```
 
 - If a name isn't defined inside a function, R looks one level up.
 
```{r}
x <- 2
g03 <- function() {
   y <- 1
   c(x, y)
}
g03()
y
```

 - R searches inside the current function, then looks where the function is defined and so on, all the way up to the global environment.
 
 - Finally, R looks in other loaded packages.

```{r}

y <- 10

f <- function(x) {
   y <- 2
   y^2 + g(x)
}

g <- function(x) {
   x * y
}

```

What is the value of `f(3)`?

### functions versus variables

 - In R, functions are ordinary objects.  This means the scoping rules described above also apply to functions.
 
 - However, rules get complicated when functions and non-functions share the same name.
 
 - Better avoid assigning same names to objects 
 
### A fresh start

 - Every time a function is called a _new_ environment is created to host its execution.

```{r}
rm(a) # just in case...
g11 <- function() {
  if (!exists("a")) {
    a <- 1
  } else {
    a <- a + 1
  }
  a
}

g11()
g11()
```

> What happens if we do 

```{r, eval=FALSE}
a <- 1:5
g11()
g11()

```

### Dynamic lookup

 - Lexical scoping determines where to look for values.
 
 - R looks for values when the function is run, not when the function is created.

```{r}
g12 <- function() x + 1
x <- 15
g12()

x <- 20
g12()
```

 - Depending on variables defined in the global environment can be bad!

 - `codetools::findGlobals()` can be helpful
 
### Default arguments

 - You can define default values for arguments
 
 - Default values can be in terms of other arguments, or even in terms of variables defined later in the function
 
 - This is because R uses **Lazy Evaluation** that function arguments are only evaluated if accessed.
 
```{r}
h04 <- function(x = 1, y = x * 2, z = a + b) {
  a <- 10
  b <- 100
  
  c(x, y, z)
}

h04()
```

### `...` (dot-dot-dot)

 - Functions can have a special argument `...`
 
 - With `...`, a function can take any number of additional arguments
 
 - You can use `...` to pass those additional arguments on to another function
 
 **Pro**
   
   - If your function takes a function as an argument, you want some way to pass additional arguments to that function.

```{r}
x <- list(c(1, 3, NA), c(4, NA, 6))
str(lapply(x, mean, na.rm = TRUE))   
```
   
 **Con**
 
   - A misspelled argument will not raise an error.

```{r}
sum(1, 2, NA, na_rm = TRUE)
```
 
## Control flow

These are the basic control-flow constructs of the R language. They function in much the same way as control statements in any Algol-like (Algol short for "Algorithmic Language") language. They are all reserved words.

| keyword | usage |
| ------- | ----- |
| if | **if**(_cond_) _expr_ |
| if-else | **if**(_cond_) _cons.expr_ **else** _alt.expr_ |
| for  | **for**(_var_ **in** _seq_) _expr_ |
| while | **while**(_cond_) _expr_ |
| break | breaks out of a _for_ loop |
| next | halts the processing of the current iteration and advances the looping index|

## Exiting a function

Most functions exit in one of two ways:

  - return a value, indicating success
 
  - throw an error, indicating failure
 
### Implicit versus explicit returns

There are two ways that a function can return a value:

  - Implicitly, where the last evaluated expression is the return value:
  
```{r}
j01 <- function(x) {
  if (x < 10) {
    0
  } else {
    10
  }
}
j01(5)
j01(15)
```
  
  - Explicitly, by calling `return()`
  
```{r}
j02 <- function(x) {
  if (x < 10) {
    return(0)
  } else {
    return(10)
  }
}
```

  - You can hide the output from automatic printing by applying `invisible()` to the last value:
  
```{r}
j04 <- function() invisible(1)
j04()
```

### Errors

If a function cannot complete its assigned task, it should throw an error with `stop()`, which immediately terminates the execution of the function.

```{r, error=TRUE}
j05 <- function() {
  stop("I'm an error")
  return(10)
}
j05()
```

### Exit handlers

  - Use `on.exit()` to set up an **exit handler** that is run regardless of whether the function exits normally or with an error
  
  - Always set `add = TRUE` when using `on.exit()`. Otherwise, each call will overwrite the previous exit handler.
  
```{r, error=TRUE}
j06 <- function(x) {
  cat("Hello\n")
  on.exit(cat("Goodbye!\n"), add = TRUE)
  
  if (x) {
    return(10)
  } else {
    stop("Error")
  }
}

j06(TRUE)

j06(FALSE)
```

  - Can use exit handler for clean-up
  
```{r, error = TRUE}
with_dir <- function(dir, code) {
  old <- setwd(dir)
  on.exit(setwd(old), add = TRUE)

  code
}

getwd()
with_dir("~", getwd())
getwd()
```


