Writing Functions (the minimum)

Silvie Cinková

2025-08-14

Why write your own

  • avoid repeated copy-pasting and adjusting of code

    • laborious and error-prone
  • you can keep a separate file with functions

    • have your private “library”/“package”

Keep functions in a separate file

  1. Write your long script.

  2. For your future self, extract the functions from it and save them in a separate bare R file (.R).

  3. In the main document with the script, apply the function source on that file. This will run the file and hence load all functions into your Global Environment.

    • source(<"path/YourFileWithFunctions">.R)

Syntax

<function_name)> <- function( arguments ) {

function body (a piece of code using the arguments)

}

Arguments

print_something <- function(some_string_provided_by_user) {
  print(some_string_provided_by_user)
}
print_something(some_string_provided_by_user = "Good morning!")
[1] "Good morning!"

Arguments with suggested options

print_two_options <- function(user_selected_string = c("hello", "hi")) {
  print(user_selected_string)
}
print_two_options(user_selected_string = "hello")
[1] "hello"
print_two_options(user_selected_string = "bye")
[1] "bye"

Limit user’s options

print_strictly_two_options <- function(user_selected_string = c("hello", "hi")) {
  if (user_selected_string %in% c("hello", "hi")) {
    print(user_selected_string)
  } else {print("This value is not allowed. Please choose `hello` or `hi`.")}
}
print_strictly_two_options("wow")
[1] "This value is not allowed. Please choose `hello` or `hi`."

Arguments with default argument values

print_in_case <- function(string_from_user, convert_q_to_upper = FALSE){
  if (require(stringr) == FALSE) {
    library(stringr)
  }
  if (convert_q_to_upper == TRUE) { 
   string_from_user <- str_replace_all(string = 
          string_from_user, pattern = "q", 
          replacement = "Q") 
   print(string_from_user)
  } else  {print(string_from_user)}
}
print_in_case("Basque")
[1] "Basque"
print_in_case("Basque", convert_q_to_upper = TRUE)
[1] "BasQue"
print_in_case("Basque", convert_q_to_upper = TRUE)
[1] "BasQue"

What functions return

result: the output of the last line

  • do not assign it to variable

  • or use return(variable) as last line

The last line: spit out the result

give_me_that <- function(string){
  toupper(string)
}
give_me_that(string = "hello")
[1] "HELLO"
result_gimmethat <- give_me_that(string = "hello")
result_gimmethat
[1] "HELLO"

The last line: return result

result_return <- function(string){
  my_func_result <- toupper(string)
  writeLines(my_func_result, con = "myresultstring.txt")
}

Writes a file in the working directory, but will not save anything to a variable.

myresult <- result_return(string = "hohoho")
myresult
NULL
list.files(pattern = "myresultstring", full.names = TRUE)
[1] "./myresultstring.txt"
file.remove("myresultstring.txt")
[1] TRUE

Visibility of outputs

  • normally a result is visible but can be made invisible

    • (e.g.print , readr::write_lines unlike writeLines returning NULL)
a <- print("hello")
[1] "hello"

That printed “hello” but also saved it into the variable:

a 
[1] "hello"

Allow for arguments of functions inside your function

  • this is called argument forwarding

  • sample() returns the whole sample by default.

allows_arguments <- function(vector, ...) {
  sample(x = vector, ...)
}
allows_arguments(vector = c(1:10), size = 3)
[1] 3 8 1
allows_arguments(vector = c(1:10))
 [1]  2  8  7  1  6  5 10  4  9  3

Scope fundamental

  • Variables defined inside a function do not exist outside the function!!!
# computes mean of a numeric vector
mean_func <- function(numvec) {
  all_summed <- sum(numvec)
  divideby = length(numvec)
  sum(numvec)/length(numvec)
} 
mean_func(numvec = c(3,2,4))
[1] 3
all_summed
Error in eval(expr, envir, enclos): object 'all_summed' not found