# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .r
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: R
#     language: R
#     name: ir
# ---

# # Lists and Functions 
#
# ## Lists
#
# `tibble`s are very powerful, but as the data in them is assumed to be tabular, they are not the most flexible of data containers.
#
# In some cases you need a `list`. 
#
# `list` is something between *list* and *dict* in Python. Compared to vectors it can have any data types in it, even other lists.

example_list <- list('a',3L,4.3,3+2i, list('b'))
str(example_list)

# `list` can also have named elements. During list creation they can be given as keyword arguments to the function call.
#
# New elements can also be created with `$name`.
#
# After `list` has been created there's quite a few ways to access its contents:
# - `[ ]` returns a slice of the list that keeps the names intact. Thus it always returns a list.
# - `[[ ]]` or `$` returns the value stored in key. Type of the object depends on the stored object.

# +
# Initialize a list with various data types
example_list2 <- list(3L,42.4,something='nothing',c(13.2,32.2),boolean=TRUE, l=list(32.2,1.6))

# Add a new variable to the list
example_list2$variable <- 3.4

# Return a slice of the list
print('Slice:')
example_list2['something']
class(example_list2['something'])

# Return the value of 'something'
print('Value:')
example_list2[['something']]
class(example_list2[['something']])
example_list2$something
class(example_list2$something)

print('The whole list')

# Print the list. Note that the named values have indices.
print(example_list2)
# -

# `tibble` is an extension to `data.frame`, which in turn is an extension to `list`. Thus these work with `tibble`s as well. We've been avoiding them as modern R uses procedural coding in order to access the elements. However, in some cases it is easier to access columns of `tibble`s with these.

# ## Functions
#
# R is a highly procedural language. This means that most of the time you'll modify data through function calls.
#
# This applies to loops and iteration as well. When encountered with a *do this for all data presented here*-situation, R programmers rarely turn to `for` or `while` loops. Instead, they use `map`- or `apply`-functions.
#
# A function is defined into a variable with the assignment operator `<-` and the `function`-statement:

# +
example_function <- function (argument, optional_argument='optional not set') {
    print(argument)
    print(optional_argument)
    return(0)
}

# Do not give an optional argument
example_function('test')
# Give arguments with positional placement
example_function('test2','optional set')
# Give arguments with keywords
example_function(optional_argument='optional_set3',argument='test3')
# Missing an argument returns an error, catch it with try()
print(try(example_function()))
# -

# Arguments are specified in the function definition. Ones with default values are considered optional. Positional arguments are evaluated from left to right. During function call one can also specify arguments by giving their names.
#
# Output of the function call is returned with a call to the `return`-function. One can also use `invisible` to return the output only if it asked during function call. Returning output is especially important if you want to write a function that works with the Tidyverse pipe. If you haven't specified a `return` or `invisible` statement, the last output of the function is returned as `invisible`.
#
# It is important to know that in R all objects are immutable. This means that if you provide a function with an object, R will copy this object once it knows it will be accessed (lazy evaluation) and any changes you make to this object are not done to the original object unless you assign the result to the original object.
#
# All R-functions do their work in an environment. If some variable has not been set in that specific environment, it will look to the environment that made the function call. Any change to these variables is made to the environment of the function.

# +
very_secret_key <- 'this is a very secret key indeed' 

peek_at_the_secret_key <- function() {
    print('I know that your secret key is:')
    print(very_secret_key)
}

modify_the_secret_key <- function() {
    very_secret_key <- 'I changed your key!'
}

# This works, function can access global environment
peek_at_the_secret_key()

# This does not work, function will write to its own environment
modify_the_secret_key()

very_secret_key
# -

# ## Oneliner functions
#
# One-liner functions can be used to create shorter code with less function definitions. By using `~` one creates a callable function and `.` refers to the element that is currently operated upon.
#
# One can also store the function created in this way into a variable.

# +
library(tidyverse)

rename_x_to_z_long <- function(name) {
    gsub('x','z',name)
}

rename_x_to_z_shorter <- function(name) gsub('x','z',name)

rename_x_to_z_even_shorter <- ~gsub('x','z',.)

a <- tibble(x=1)

a %>%
    rename_all(rename_x_to_z_long)

a %>%
    rename_all(rename_x_to_z_shorter)

a %>%
    rename_all(rename_x_to_z_even_shorter)

a %>%
    rename_all(~gsub('x','z',.))

StarcoderdataJupyterScriptsDedupFiltered