---
title: "Vectorization &<br/>Control Flow"
subtitle: "Lecture 02"
author: "Dr. Colin Rundel"
footer: "Sta 323 - Spring 2026"
format:
  revealjs:
    theme: slides.scss
    transition: fade
    slide-number: true
    self-contained: true
execute:
  echo: true
  warning: true
engine: knitr
---

```{r setup}
#| message: False
#| warning: False
#| include: False
options(
  htmltools.dir.version = FALSE, # for blogdown
  width=80
)
```

## From last time - Exercise 1 

#### Part 1

What is the type of the following vectors? Explain why they have that type.

```{r}
#| eval: false
c(1, NA+1L, "C")
c(1L / 0, NA)
c(1:3, 5)
c(3L, NaN+1L)
c(NA, TRUE)
```

#### Part 2

Considering only the four (common) data types, what is R's implicit type conversion hierarchy (from highest priority to lowest priority)? 

::: {.aside}
*Hint* - think about the pairwise interactions between types.
:::



# Logical & Comparison operators


## Logical (boolean) operators

<br/>

|  Operator                     |  Operation    |  Vectorized? |
|:-----------------------------:|:-------------:|:------------:|
| <code>x &#124; y</code>       |  or           |   Yes        |
| `x & y`                       |  and          |   Yes        |
| `!x`                          |  not          |   Yes        |
| <code>x &#124;&#124; y</code> |  or           |   No         |
| `x && y`                      |  and          |   No         |
|`xor(x, y)`                    |  exclusive or |   Yes        |




## Vectorized?

::: {.small}
```{r}
x = c(TRUE,FALSE,TRUE)
y = c(FALSE,TRUE,TRUE)
```
:::

. . .

:::: {.columns .small}
::: {.column width='50%'}
```{r}
x | y
x & y
```
:::

::: {.column .fragment width='50%'}
```{r}
#| error: true
x || y
x && y
TRUE && FALSE
```
:::
::::

. . .

Before R 4.3 both `||` and `&&` only used the *first* value in the vector, all other values are ignored and there was no warning about the ignored values.

`&` and `|` are almost always going to be the right choice, the only time we use `&&` or `||` is when you need to take advantage of [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation).


## Vectorization and math

Almost all of the basic mathematical operations (and many other functions) in R are vectorized.

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
c(1, 2, 3) + c(3, 2, 1)
c(1, 2, 3) / c(3, 2, 1)
```
:::

::: {.column width='50%'}
```{r}
log(c(1, 3, 0))
sin(c(1, 2, 3))
```
:::
::::



## Length coercion (aka recycling)

If the lengths of the vector do not match, then the shorter vector has its values recycled to match the length of the longer vector.

::: {.small}
```{r}
x = c(TRUE, FALSE, TRUE)
y = c(TRUE)
z = c(FALSE, TRUE)
```
:::

. . .

:::: {.columns .small}
::: {.column width='50%'}
```{r}
x | y
x & y
```
:::

::: {.column width='50%'}
```{r}
y | z
y & z
```
:::
::::

. . .

::: {.small}
```{r}
x | z
```
:::



## Length coercion and math

The same length coercion rules apply for most basic mathematical operators,

::: {.small}
```{r}
x = c(1, 2, 3)
y = c(5, 4)
z = 10L
```
:::

:::: {.columns .small}
::: {.column width='50%'}
```{r}
x + x
x + z
```
:::

::: {.column width='50%'}
```{r}
y / z
log(x)+z
```
:::
::::

. . .

::: {.small}
```{r}
x %% y
```
:::




## Comparison operators

<br/>

|  Operator  |  Comparison                |  Vectorized?
|:----------:|:---------------------------:|:---------------:
| `x < y`    |  less than                 |  Yes
| `x > y`    |  greater than              |  Yes
| `x <= y`   |  less than or equal to     |  Yes
| `x >= y`   |  greater than or equal to  |  Yes
| `x != y`   |  not equal to              |  Yes
| `x == y`   |  equal to                  |  Yes
| `x %in% y` |  contains                  |  Yes (over `x`)$^*$

::: {.aside}
$^*$over `x` means the returned value will have the length of `x` regardless of the length of `y`
:::



## Comparisons

::: {.small}
```{r}
x = c("A","B","C")
y = c("A")
```
:::



:::: {.columns .small}
::: {.column width='50%'}
```{r}
x == y
x != y
```
:::

::: {.column width='50%'}
```{r}
x %in% y
y %in% x
```
:::
::::

. . .

Type coercion also applies for comparison operators which can result in *interesting* behavior

:::: {.columns .small}
::: {.column width='50%'}
```{r}
TRUE == "TRUE"
FALSE == 1
```
:::

::: {.column width='50%'}
```{r}
TRUE == 1
TRUE == 5
```
:::
::::


## `>` & `<` with characters

While perhaps unexpected, these comparison operators can be used with character values.

:::: {.columns}
::: {.column width='50%'}
```{r}
"A" < "B"
"A" > "B"
"A" < "a"
"a" > "!"
```
:::

::: {.column width='50%'}
```{r}
"Good" < "Goodbye"
c("Alice", "Bob", "Carol") <= "B"
```
:::
::::

::: {.aside}
Note - to better understand how this works, i.e. the ordering used, see [ASCII code](https://www.ascii-code.com/) and https://stackoverflow.com/questions/7229408/what-are-the-r-sorting-rules-of-character-vectors
:::



# Control Flow


## Conditional Control Flow

Conditional execution of code blocks is achieved via `if` statements. 

```{r}
x = c(1, 3)
```

. . .

:::: {.columns}
::: {.column width='50%'}
```{r}
if (3 %in% x) {
  print("Contains 3!")
}
```
:::

::: {.column width='50%'}
```{r}
if (1 %in% x)
  print("Contains 1!")
```
:::
::::

. . .

:::: {.columns}
::: {.column width='50%'}
```{r}
if (5 %in% x) {
  print("Contains 5!")
}
```
:::

::: {.column width='50%'}
```{r}
if (5 %in% x) {
  print("Contains 5!")
} else {
  print("Does not contain 5!")
}
```
:::
::::



## `if` is *not* vectorized

::: {.small}
```{r}
x = c(1, 3)
```
:::

. . .

::: {.small}
```{r}
#| error: True
if (x == 1)
  print("x is 1!")
```
:::

. . .

::: {.small}
```{r}
#| error: True
if (x == 3)
  print("x is 3!")
```
:::

. . .

<br/>

This behavior (throwing an error) was added in R 4.2, previous versions will only emit a warnings (while using the first value in the condition vector).



## Collapsing logical vectors

There are a couple of helpful functions for collapsing logical vectors: `any`, `all`

```{r}
x = c(3,4,1)
```



:::: {.columns}
::: {.column width='50%'}
```{r}
x >= 2
any(x >= 2)
all(x >= 2)
```
:::

::: {.column width='50%'}
```{r}
x <= 4
any(x <= 4)
all(x <= 4)
```
:::
::::

. . .

```{r}
if (any(x == 3)) 
  print("x contains 3!")
```



## `else if` and `else`

:::: {.columns}
::: {.column width='50%'}
```{r}
x = 3

if (x < 0) {
  "x is negative"
} else if (x > 0) {
  "x is positive"
} else {
  "x is zero"
}
```
:::

::: {.column width='50%'}
```{r}
x = 0

if (x < 0) {
  "x is negative"
} else if (x > 0) {
  "x is positive"
} else {
  "x is zero"
}
```
:::
::::



## `if` blocks return a value

R's `if` conditional statements return a value (invisibly), the two following implementations are equivalent.

:::: {.columns}
::: {.column width='50%'}
```{r}
x = 5
```

```{r}
s = if (x %% 2 == 0) {
  x / 2
} else {
  3*x + 1
}
```

```{r}
s
```
:::

::: {.column width='50%'}
```{r}
x = 5
```

```{r}
if (x %% 2 == 0) {
  s = x / 2
} else {
  s = 3*x + 1
}
```

```{r}
s
```
:::
::::

::: {.aside}
Notice that conditional expressions are evaluated in the parent scope.
:::



## Exercise 1

Take a look at the following code below on the left, without running it in R what do you expect the outcome will be for each call on the right?

:::: {.columns .small}
::: {.column width='50%'}
```r
f = function(x) {
  # Check small prime
  if (x > 10 || x < -10) {
    stop("Input too big")
  } else if (x %in% c(2, 3, 5, 7)) {
    cat("Input is prime!\n")
  } else if (x %% 2 == 0) {
    cat("Input is even!\n")
  } else if (x %% 2 == 1) {
    cat("Input is odd!\n")
  }
}
```
:::

::: {.column width='50%'}
```r
f(1)
f(3)
f(-1)
f(-3)
f(1:2)
f("0")
f("zero")
```
:::
::::


```{r}
#| echo: False
countdown::countdown(minutes = 3)
```



## Conditionals and missing values

`NA`s can be particularly problematic for control flow,

:::: {.columns .small}
::: {.column width='50%'}
```{r}
#| error: True
if (2 != NA) {
  "Here"
}
```
:::

::: {.column width='50%'}
```{r}
#| error: True
2 != NA
```
:::
::::


. . .

:::: {.columns .small}
::: {.column width='50%'}

```{r}
#| error: True
if (all(c(1,2,NA,4) >= 1)) {
  "There"
}
```
:::

::: {.column width='50%'}

```{r}
#| error: True
all(c(1,2,NA,4) >= 1)
```
:::
::::

. . .

:::: {.columns .small}
::: {.column width='50%'}
```{r}
#| error: True
if (any(c(1,2,NA,4) >= 1)) {
  "There"
}
```
:::

::: {.column width='50%'}
```{r}
#| error: True
any(c(1,2,NA,4) >= 1)
```
:::
::::

## Testing for `NA`

To explicitly test if a value is missing it is necessary to use `is.na` (often along with `any` or `all`).

:::: {.columns}
::: {.column width='50%'}
```{r}
NA == NA
is.na(NA)
is.na(1)
```
:::

::: {.column width='50%'}
```{r}
is.na(c(1,2,3,NA))
any(is.na(c(1,2,3,NA)))
all(is.na(c(1,2,3,NA)))
```
:::
::::

. . . 

Note `is.na()` is testing for a property of the *values*, **not** a property of the *vector* - so it is *vectorized*.


# Errors

## `stop` and `stopifnot`

Often we want to validate user input, function arguments, or other assumptions in our code - if our assumptions are not met then we often want to report (throw) an error and stop execution. 

::: {.medium}
```{r}
#| error: True
ok = FALSE
```
:::

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
#| error: True
if (!ok)
  stop("Things are not ok.")
```
:::

::: {.column width='50%'}
```{r}
#| error: True
stopifnot(ok)
```

```{r}
#| error: True
stopifnot("Still not ok" = ok)
```
:::
::::






::: {.aside}
*Note* - an error (like the one generated by `stop`) will prevent an RMarkdown or Quarto document from rendering unless `error = TRUE` or `#| error: true` is set for that code chunk.
:::



## Style choices

::::: {.columns .medium}
::: {.column width='50%'}
Do stuff:
```{r}
#| eval: False
if (condition_one) {
  ## Do stuff
} else if (condition_two) {
  ## Do other stuff
} else if (condition_error) {
  stop("Condition error occurred")
}
```
:::

::: {.column width='50%' .fragment}
Do stuff (better):
```{r}
#| eval: False
# Do stuff better
if (condition_error) {
  stop("Condition error occurred")
}

if (condition_one) {
  ## Do stuff
} else if (condition_two) {
  ## Do other stuff
}
```
:::
::::


## Why errors?

R has a variety of different output "methods" that can be used,

* Printed output - `cat()`, `print()`

* Diagnostic messages - `message()`

* Warnings - `warning()`

* Errors - `stop()`, `stopifnot()`

Each of these provides text output while also providing signals which can be interacted with programmatically (e.g. catching errors or treating warnings as errors).


## Handling errors

::: {.small}
```{r}
flip = function() {
  if (runif(1) > 0.5) 
    stop("Heads") 
  else 
    "Tails"
}
```
:::



```{r}
#| include: false
set.seed(123)
```

:::: {.columns .small}
::: {.column width='50%'}
```{r}
#| error: true
flip()
```
:::

::: {.column width='50%'}
```{r}
#| error: true
flip()
```
:::
::::

. . .

```{r}
#| include: false
set.seed(123)
```

:::: {.columns .small}
::: {.column width='50%'}
```{r}
#| error: true
x = try(flip(), silent=TRUE)
str(x)
```
:::

::: {.column width='50%'}
```{r}
#| error: true
x = try(flip(), silent=TRUE)
str(x)
```
:::
::::




::: {.aside}
The more powerful version of `try()` is `tryCatch()` but it is beyond the scope of this lecture
:::

# Functions

## What is a function?

Functions are abstractions in programming languages that allow us to modularize our code into small "self contained" units.

In general the goals of writing functions is to,

* Simplify a complex process or task into smaller sub-steps

* Allow for the reuse of code without duplication

* Improve the readability of your code

* Improve the maintainability of your code

## Functions as objects

Functions are first-class objects in R and have a *mode* of `function`. They are assigned names like other objects using `=` or `<-`.

::: {.medium}
```{r}
gcd = function(x1, y1, x2 = 0, y2 = 0) {
  # x and y values should be in radians
  R = 6371 # Earth mean radius in km

  # distance in km
  acos(sin(y1)*sin(y2) + cos(y1)*cos(y2) * cos(x2-x1)) * R
}
```
:::

. . .

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
typeof(gcd)
```
:::
::: {.column width='50%'}
```{r}
mode(gcd)
```
:::
::::

::: {.aside}
We use mode here because there are multiple kinds of "functions" in R (see `builtin`, `special`, and `primitive`)
:::


## Function elements

::: text
In R functions are defined by *two* components: 

1. the arguments (`formals`) 

2. the code / expression (`body`). 
:::

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
str( formals(gcd) )
```
:::
::: {.column width='50%'}
```{r}
body(gcd)
```
:::
::::

. . . 

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
typeof( formals(gcd) )
```
:::
::: {.column width='50%'}
```{r}
typeof( body(gcd) )
```
:::
::::


::: {.aside}
Note when using `body()` here the code we get back has had comments removed, if you want to access the full code you can use `attr(gcd, "srcref")`.
:::


## Return values

As with most other languages, functions are most often used to process inputs and return a value as output. There are two approaches to returning values from functions in R - *explicit* and *implicit* returns.

. . .

:::: {.columns}
::: {.column width='50%'}
**Explicit** - using one or more `return` calls
:::

::: {.column width='50%'}
**Implicit** - return value of the last expression is returned.
:::
::::

:::: {.columns}
::: {.column width='50%'}
```{r}
f = function(x) {
  return(x * x)
}
```
```{r}
f(2)
```
:::

::: {.column width='50%'}

```{r}
g = function(x) {
  x * x
}
```
```{r}
g(3)
```
:::
::::

::: {.aside}
Most expressions in R return a value even if this may not be obvious at the time
:::


## Invisible returns

Many functions in R make use of an invisible return value

::: {.columns}
::: {.column}
```{r}
invisible(1)
```
:::
::: {.column}
```{r}
print(invisible(1))
```
:::
:::

. . .


:::: {.columns}
::: {.column width='50%'}
```{r}
f = function(x) {
  x
}
```
```{r}
f(1)
y = f(1)
y
```
:::

::: {.column width='50%'}
```{r}
g = function(x) {
  invisible(x)
}
```

```{r}
g(1)
```

```{r}
z = g(1)
z
```
:::
::::



## Returning multiple values

If we want a function to return more than one value we can group results using atomic vectors or lists.

:::: {.columns}
::: {.column width='50%'}
```{r}
f = function(x) {
  c(x, x^2, x^3)
}

f(1:2)
```
:::

::: {.column width='50%'}
```{r}
g = function(x) {
  list(x, "hello")
}

g(1:2)
```
:::
::::

::: {.aside}
More on `list()`s next time
:::


## Argument names

When defining a function we explicitly define names for the arguments, which become variables within the scope of the function.

When calling a function we can use these names to pass arguments in an alternative order.


::: {.medium}
```{r}
f = function(x, y, z) {
  paste0("x=", x, " y=", y, " z=", z)
}
```
:::

. . .

:::: {.columns .medium}
::: {.column width='50%'}
```{r, error=TRUE}
f(1, 2, 3)
f(z=1, x=2, y=3)
f(1, 2, 3, 4)
```
:::
::: {.column width='50%'}
```{r, error=TRUE}
f(y=2, 1, 3)
f(y=2, 1, x=3)
f(1, 2, m=3)
```
:::
::::


## Argument defaults

It is also possible to give function arguments default values, so that they don't need to be provided every time the function is called.

```{r}
#| error: True
f = function(x, y=1, z=1) {
  paste0("x=", x, " y=", y, " z=", z)
}
```

. . .

:::: {.columns}
::: {.column width='50%'}
```{r}
#| error: True
f(3)
f(x=3)
```
:::
::: {.column width='50%'}
```{r}
#| error: True
f(z=3, x=2)
f(y=2, 2)
```
:::
::::

. . .

```{r, error=TRUE}
f()
```

::: {.aside}
This ability to freely mix the ordering of named and unnamed arguments is fairly *unique* to R
:::



## Scope

R has generous scoping rules, if it can't find a variable in the current scope (e.g. a function's body) it will look for it in the next higher scope, and so on until an object with that name is found or it runs out of environments.

:::: {.columns}
::: {.column width='50%'}
```{r}
y = 1

f = function(x) {
  x + y
}
```
```{r}
f(3)
```
:::

::: {.column width='50%' .fragment}
```{r}
y = 1

g = function(x) {
  y = 2
  x + y
}
```
```{r}
g(3)
y
```
:::
::::

## Scope persistance

Additionally, variables defined within a scope only persist for the duration of that scope, and do not overwrite variables at higher scope(s).

:::: {.columns}
::: {.column width='50%'}
```{r}
x = 1
y = 1
z = 1

f = function() {
    y = 2
    g = function() {
      z = 3
      return(x + y + z)
    }
    return(g())
}
```
:::
::: {.column width='50%'}
```{r}
f()

c(x,y,z)
```
:::
::::


::: {.aside}
R supports global assignment via `<<-`, generally using global variables is considered bad practice and should be avoided.
:::


## Lazy evaluation

Another interesting / unique feature of R is that function arguments are lazily evaluated, which means they are only evaluated when needed.

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
f = function(x) {
  TRUE
}
```
:::
::: {.column width='50%'}
```{r}
g = function(x) {
  x
  TRUE
}
```
:::
::::

. . .


:::: {.columns .medium}
::: {.column width='50%'}
```{r}
f(1)
```
:::
::: {.column width='50%'}
```{r}
g(1)
```
:::
::::

. . .

:::: {.columns .medium}
::: {.column width='50%'}
```{r}
#| error: True
f(stop("Error"))
```
:::
::: {.column width='50%'}
```{r}
#| error: True
g(stop("Error"))
```
:::
::::

## More "practical" lazy evaluation

The previous example is not particularly useful, a more common use for this lazy evaluation is that this enables us define arguments as expressions of other arguments.

```{r}
f = function(x, y=x+1, z=1) {
  x = x + z
  y
}
```
```{r}
f(x=1)
f(x=1, z=2)
```


## Operators as functions

In R, operators are actually a special type of function - using backticks around the operator we can write them as functions.
 
```{r}
`+`
typeof(`+`)
```

. . .

```{r}
x = 4:1
x + 2
`+`(x, 2)
```




## Getting Help

Prefixing any function name with a `?` will open the related help file for that function.

```{r, eval=FALSE}
?`+`
?sum
```

. . .

For functions not in the base package, you can generally see their implementation by entering the function name without parentheses (or using the `body` function).

::: {.small}
```{r}
lm
```
:::



## Less Helpful Examples

```{r}
list

`[`

sum

`+`
```


::: {.aside}
For the curious the [lookup](https://github.com/jimhester/lookup) package will help you track down the source code of these functions.
:::


## Infix functions (operators)

We can define our own infix functions like `+` or `*`, the only requirement is that the function name must start and end with a `%`.

```{r}
`%nand%` = function(x, y) {
  !(x & y)
}
```

:::: {.columns}
::: {.column width='50%'}
```{r}
TRUE %nand% TRUE
TRUE %nand% FALSE
```
:::

::: {.column width='50%'}
```{r}
FALSE %nand% TRUE
FALSE %nand% FALSE
```
:::
::::


## Replacement functions 

We can also define functions that allow for 'inplace' modification like `attr` or `names`.

:::: {.columns .small}
::: {.column width='50%'}
```{r}
`last<-` = function(x, value) {
  x[length(x)] = value
  x
}
```
```{r}
x = 1:10
```
```{r}
#| error: true
last(x) = 5L
x
last(x) = NA
x
last(1)
```
:::

::: {.column width='50%' .fragment}
```{r}
`modify<-` = function(x, pos, value) {
  x[pos] = value
  x
}
```
```{r}
x = 1:10
```
```{r}
modify(x,1) = 5L
x
modify(x, 9:10) = 1L
x
```
:::
::::






# Loops


## for loops

These are the most common type of loop in R - given a vector it iterates through the elements and evaluate the code expression for each value.

::: {.small}
```{r}
is_even = function(x) {
  res = c()
  
  for(val in x) {
    res = c(res, val %% 2 == 0)
  }
  
  res
}
```
```{r}
is_even(1:10)
is_even(seq(1,5,2))
```
:::




## `while` loops

This loop repeats evaluation of the code expression until the condition is **not** met (i.e. evaluates to `FALSE`)

::: {.small}
```{r}
make_seq = function(from = 1, to = 1, by = 1) {
  res = c(from)
  cur = from
  
  while(cur+by <= to) {
    cur = cur + by
    res = c(res, cur)
  }
  
  res
}
```
```{r}
make_seq(1, 6)
make_seq(1, 6, 2)
```
:::


## `repeat` loops

Equivalent to a `while(TRUE){}` loop, it repeats until a `break` statement

::: {.small}
```{r}
make_seq2 = function(from = 1, to = 1, by = 1) {
  res = c(from)
  cur = from
  
  repeat {
    cur = cur + by
    if (cur > to)
      break
    res = c(res, cur)
  }
  
  res
}
```
```{r}
make_seq2(1, 6)
make_seq2(1, 6, 2)
```
:::

## Special keywords - `break` and `next`

These are special actions that only work *inside* of a loop

:::: {.columns}
::: {.column width='50%'}
* `break` - ends the current **loop**
:::

::: {.column width='50%'}
* `next` - ends the current **iteration**
:::
::::



:::: {.columns .small}
::: {.column width='50%'}
```{r}
f = function(x) {
  res = c()
  for(i in x) {
    if (i %% 2 == 0)
      break
    res = c(res, i)
  }
  res
}
```
```{r}
f(1:10)
f(c(1,1,1,2,2,3))
```
:::

::: {.column width='50%' .fragment}
```{r}
g = function(x) {
  res = c()
  for(i in x) {
    if (i %% 2 == 0)
      next
    res = c(res,i)
  }
  res
}
```
```{r}
g(1:10)
g(c(1,1,1,2,2,3))
```
:::
::::


## Some helpful functions

Often we want to use a loop across the indexes of an object and not the elements themselves. There are several useful functions to help you do this: 

`:`, `length`, `seq`, `seq_along`, `seq_len`, etc.

:::: {.columns}
::: {.column width='50%'}
```{r}
4:7
length(4:7)
seq(4,7)
```
:::
::: {.column width='50%'}
```{r}
seq_along(4:7)
seq_len(length(4:7))
seq(4,7,by=2)
```
:::
::::




## Avoid using `1:length(x)`

A common loop construction you'll see in a lot of R code is using `1:length(x)` to generate a vector of index values for the vector `x`. 

:::: {.columns .small}
::: {.column width='50%'}
```{r}
f = function(x) {
  for(i in 1:length(x)) {
    print(i)
  }
}
```
```{r}
f(2:1)
```
:::

::: {.column width='50%' .fragment}
```{r}
g = function(x) {
  for(i in seq_along(x)) {
    print(i)
  }
}
```
```{r}
g(2:1)
```
:::
::::

. . .

:::: {.columns .small}
::: {.column width='50%'}
```{r}
f(2)
```
:::

::: {.column width='50%' .fragment}
```{r}
g(2)
```
:::
::::

. . .

:::: {.columns .small}
::: {.column width='50%'}
```{r}
f(integer())
```
:::

::: {.column width='50%' .fragment}
```{r}
g(integer())
```
:::
::::


## What was the problem?

```{r}
length(integer())
1:length(integer())
seq_along(integer())
```



## Exercise 2

:::: {.columns .medium}
::: {.column width='40%'}
To the right is a vector containing all prime numbers between 2 and 100 and a separate vector `x` containing some values we would like to check for primality.

Write the R code necessary to print only the values of `x` that are *not* prime (without using subsetting or the `%in%` operator). 

Your code will need to use *nested* loops to iterate through the vector of `primes` and `x`.

:::

::: {.column .small width='60%'}
```{r}
primes = c( 2,  3,  5,  7, 11, 13, 17, 19, 23, 
           29, 31, 37, 41, 43, 47, 53, 59, 61, 
           67, 71, 73, 79, 83, 89, 97)

x = c(3,4,12,19,23,51,61,63,78)
```
:::
::::

```{r}
#| echo: false
countdown::countdown(3)
```


