Functions Review

Let’s look at a very simple function:

f <- function(x){
  x ** 2 + 1
}

Here’s the process by which the value of this function is computed:

We want to compute f(5)

We substitute x <- 5

The value the function computes is \(5^2 + 1 = 26\).

f(5) is evaluated to 26.

This is the same process that you’d know from algebra when computing \(g(x)\) for \(g(x) = x^2 + 1\).

Local variables and “scratch work”

Here is a slightly more complicated function

h <- function(x){
  y <- 2 * x
  y ** 2 - x
}

Here, we defined a local variable y to help us with the computation. The process we use is

We want to compute h(2)

We substitute x <- 2

We evaluate and substitue y <- 2 * 2 (i.e., 4)

The value the function computes is \(4^2 - 2 = 14\)

The value of h(2) was evaluated to 14.

Note that we cannot access y outside of the function h. That makes sense: y is defined in terms of the parameter x, which might change.

Computing values vs printing them to the screen

Consider the following two functions:

emo.state <- function(score){
  if(score >= 98){
    "Hooray"
  }else if(score >= 95){
    "OK"
  }else{
    "Alas"
  }
}
emo.state.2 <- function(score){
  if(score >= 98){
    cat("Hooray")
  }else if(score >= 95){
    cat("OK")
  }else{
    cat("Alas")
  }
}

The function emo.state computes a value, just like our functions f and h above. For example, emo.state(99) evaluates to "Hooray!". You can see this in the following example:

a <- emo.state(99)
cat(a)
## Hooray

We first evaluated emo.state(99) and put it in a. Then we gave an instruction to R to print the value of a to the screen.

On the other hand, consider this piece of code:

a <- emo.state.2(99)
## Hooray

This already had the effect of printing an output to the screen. That’s because when R sees cat("Hooray"), it outputs Hooray to the screen. But the value that the function computes, which is cat("Hooray") is not "Hooray". In fact, it is

a
## NULL

You will very rarely need to use cat inside functions. Outside of Precept 1, you just should not use it at all in this course.

secret.number example

This is an example of a function where we do a lot of “scratch work”. The last line in the body of the function is the value that the function computes.

guess.secret.number <- function(secret.num){
  res1 <- secret.num + 8
  res2 <- res1 * 2
  res3 <- res2 / 4
  res3 - secret.num / 2
}

Note that guess.secret.number always computes 4, regardless of what secret.number you plug in. That is how the algebra works out!

Comments

Comments are meant to help humans understand R code. Anything that follow the pound sign # is ignored by R. You can use comments to explain your code (to your partner, preceptor, or your future self), or to temporarily disable parts of your code.

cat("hi") # This part will be ignored by R
## hi
# You can write comments here too
cat("Things are happenning again")
## Things are happenning again

Syntax

“Syntax” is the set of rules according to which R statements must be constructed. For example, as we saw in the previous lecture, to construct an if-statement, you must write things as

if(<CONDITION1>){
  <STATEMENT1>
  <STATEMENT2>
  ...
}else if(<CONDITION2>){
  <STATEMENT3>
  <STATEMENT4>
  ...
}else if(<CONDITION3>){
  <STATEMENT5>
  <STATEMENT6>
  ...
}...else{
 <STATEMENT7>
 ...
}

The curly braces and brackets are mandatory. Violating syntax rules would usually result in R producing an error.

(Syntax is distinct from semantics – the rules that determine the meaning of R statements. This is analoguous to terms used for human languages.)

Vectors

Here is an example of a vector

offer <- c(241, 590, 533, 425, 261)

A vector is a sequence of values of the same type (you can also have vectors of characters). Here is how you can access elements of a vector:

offer[1]
## [1] 241
offer[4]
## [1] 425

You can find the length (i.e., the number of elements of a vector) like so:

length(offer)
## [1] 5

Aside: technically, everything in R is a vector. It’s just that some vectors have length 1.

a <- 42
a[1]
## [1] 42

You can even do the following, if you insist:

42[1]
## [1] 42

Here are some things we can do with vectors:

sort(offer) # Compute a sorted version of the vector, in increasing order
## [1] 241 261 425 533 590
unique(c(1, 2, 1, 4, 5, 2)) # Get a vector with every element of the input appearing once
## [1] 1 2 4 5
max(offer)
## [1] 590
min(offer)
## [1] 241

Here is how we can compute the max without using the max function:

sort(offer)[length(offer)]
## [1] 590

(Explain this!)

We can also perform operations on all the elements of a vector.

offer > 533
## [1] FALSE  TRUE FALSE FALSE FALSE
offer == 533
## [1] FALSE FALSE  TRUE FALSE FALSE

Indexing vectors with logical vectors

A powerful way of extracting elements from a vector is by indexing the vector using logical vectors. Here is how we can get the third and fourth elements of the vector:

offer[c(F, T, T, F, F)]
## [1] 590 533

Of course, we could compute the logical vector above using

offer > 500
## [1] FALSE  TRUE  TRUE FALSE FALSE

Combining those, we can go

offer[offer > 500]
## [1] 590 533

Note what we did: we extracted the elements that are larger than 500.

Operating on logical values

Suppose we wanted something fancier: extracting all elements between 400 and 550. In other words, suppose we want the elements that are both larger than 400 and smaller than 550. To achieve this, we would want to combine the conditions “larger than 400” and “smaller than 550”.

We can do this using logical operators

AND:    a & b. TRUE only if a is TRUE and b is TRUE. FALSE otherwise
OR:     a | b. TRUE if at least one of a or b is TRUE, FALSE otherwise
NOT:    !a. TRUE if a is FALSE, FALSE if a is TRUE

Some examples:

pie <- TRUE
icecream <- FALSE
pie | icecream
## [1] TRUE
pie <- FALSE
icecream <- FALSE
pie | icecream
## [1] FALSE
pie <- TRUE
icecream <- FALSE
pie & icecream
## [1] FALSE
pie <- TRUE
icecream <- TRUE
pie | icecream
## [1] TRUE

Note: this is not quite how it works in English. If I say I will have pie or icecream, and then have both, that means what I said wasn’t true. But for R, the expression pie | icecream is TRUE. Technically, | is called “inclusive OR” (as opposed to the “exclusive OR” we usually mean in English.)

pie <- FALSE
icecream <- TRUE
pie | icecream
## [1] TRUE
pie <- FALSE
icecream <- FALSE
pie | icecream
## [1] FALSE
pie <- TRUE
!pie
## [1] FALSE

Inclusive OR and exclusive OR

So how do you make an expression in R that corresponds to “I will have ice cream or pie”? That is, we want to write an expression that will be TRUE whenever pie or icecream are true, but not both.

Here are several ways of accomplishing this. They all do the same thing

(pie | ice.cream) & !(pie & ice.cream)
(pie == T & ice.cream == F) | (pie == F & ice.cream == T)
(pie & !ice.cream) | (!pie & ice.cream)
pie != ice.cream