Lecture 4: Functions, Branching and Loops
August 6, 2026
π Last lecture we started exploring higher-dimensional data structures such as:
π This lecture we look into automating repetitive tasks using:
if, else, else if)for, while, repeat)
In programming, a function is a self-contained, reusable block of code designed to perform a specific task.
We have implicitly and explicitly used dozens of functions already:
Constructing Objects
c(), matrix()array(), list()Math & Summaries
exp(), log(), sqrt()mean(), median(), sum()Weβve also used functions that interact with our environment rather than our data, such as getwd() and setwd().
We define a function in R using the following syntax:
Example β Matrix Deconstruct Function
We wish to define a function that takes a matrix as an input and returns the first and last rows.
π Notice that the function we define is listed in the environment pane, just like any other object.
To test our function we define a \(4\times 3\) matrix and apply our function to obtain:
[[1]]
[1] 1 5 9
[[2]]
[1] 4 8 12
We can access each element individually using double-bracket indexing:
[1] 1 5 9
[1] 4 8 12
Letβs stress-test with an edge case: a single-row matrix.
02:00
Take 2 minutes to attempt to define your own function.
We wish to write a function called inc_nth_root which does the following:
v and n).v by 1 (i.e. adds 1 to v).v (i.e. v raised to the power of \(1/n\)).First Solution
The first solution we write is very explicitly clear but slightly inefficient:
We test our solution with the following unit tests:
[1] 4 4
[1] 2 2
When defining functions we often have cases where we typically only use a handful of the arguments since the others have default inputs.
Improving inc_nth_root
Currently if we omit our inputs we get an error message:
We can define default values for our inputs to avoid this problem:
We have specified that if no input is provided, the function will assume v = 0 and n = 2.
To protect our function against other problems (such as incorrect data types) we introduce the concept of branching.
R allows branching blocks using if, else and else if statements which each do the following:
if β takes a logical input and if the input is satisfied executes the branched code.else if β follows an if statement as a catch all, applying a second logical test when the preceding condition is not satisfied.else β the final part of a branching block: if none of the previous branches were triggered this code is executed.Below is a full branching block (an if/else block) using all three:
Suppose we wish to design a function that returns certain character strings describing whether the length of our vector x is below, between or above certain values.
Notice we defined a default input value of 0 to avoid error messages!
inc_nth_rootReturning to our inc_nth_root function we wish to make it robust to the following problems:
Inf.We can now fix these issues by using branching blocks.
π Notice is.numeric(v) == FALSE | length(v) != 1 is a reusable check β if we needed it in several functions, we could write it once as its own function and nest it inside each one.
Functions can call other functions inside their body β this is known as nesting functions.
Suppose several of our functions need to check whether an input is a valid numeric scalar. Rather than repeating the check each time, we write it once:
We can now nest numeric_scalar() inside inc_nth_root instead of repeating its logic:
inc_nth_root05:00
Try to define a function named is_divisible that takes two integer inputs p and q that behaves as follows:
p or q are not numeric then print βError: invalid input!βp divides q then print βp divides q!βp does not divide q then print βp does not divide q!βHint: The modulo operator %% helps determine when p divides q:
[1] 0
[1] 1
One of the primary advantages of programming is the ability to iterate actions rather than repeating them manually.
Loops
In programming, iterative structures are called loops, and R has three types of looping structure:
The most common looping structure in R is the for loop, which has the following general syntax:
i, j, k for the counter and specify the range with some vector.i or not, depending on the functionality of the loop.We wish to construct a for loop that:
iris data set; andAnother helpful looping structure in R is the while loop, which has the following general syntax:
TRUE.This loop never updates i, so the termination condition is never met β it will run forever.
The final looping structure we consider is the repeat loop, which has the following general syntax:
Everything we have seen so far is a type of control flow:
Other types of control flow include:
Whenever you write or read a block of branching or looping code, it helps to explicitly ask yourself:
if/for blocks where possible; nesting makes code harder to read, debug, and test.n = 0 examples!R is built around vectorized operations β many tasks that βfeelβ like they need a loop already have a built-in vectorized solution.
π Prefer vectorized functions over loops in R when one is available β they are usually faster and more concise.
03:00
Create a function star_triangle that takes a numerical input \(n\) and returns a right triangle made up of \(n\) rows of stars in the format shown below:
[1] "*"
[1] "*" "*"
[1] "*" "*" "*"
[1] "*" "*" "*" "*"
[1] "*" "*" "*" "*" "*"
[1] "*" "*" "*" "*" "*" "*"
Donβt worry about error messages, just assume the input will always be some integer.
π€ Today we began exploring automation:
π€© Next class we look at how these techniques are used to solve problems by applying: