bookclub-advr

DSLC Advanced R Book Club
git clone https://git.eamoncaddigan.net/bookclub-advr.git
Log | Files | Refs | README | LICENSE

commit c2e48aaa8c37ee29c73b89ccb6945a7774d12a56
parent 3803e947e50d98dd9a4ba24b186f64dcd9fb8f25
Author: DrEntropy <DrEntropy@users.noreply.github.com>
Date:   Sun, 19 Feb 2023 14:28:34 -0700

Edited for Cohort 7 (#48)


Diffstat:
M11_Function_operators.Rmd | 154++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 125 insertions(+), 29 deletions(-)

diff --git a/11_Function_operators.Rmd b/11_Function_operators.Rmd @@ -2,58 +2,89 @@ **Learning objectives:** -- define of function operator -- find existing function operators -- how to make your own function operator +- Define function operator +- Explore some existing function operators +- Make our own function operator -## Introduction +## Introduction -In this chapter we'll learn how to use **function operator** as a case of **function factories**. - -And we'll see a case study about how to download many web pages using a function operator. +<!--- ```{r 10-01, fig.align='center',fig.dim="50%", fig.cap="Credits: 2001-2003 Michael P.Frank (https://slideplayer.com/slide/17666226/)",echo=FALSE} +knitr::include_graphics("images/11-maths_example.png") +``` ---> -## Definition and practical examples +- A **function operator** is a function that takes one (or more) functions as input and returns a function as output. +- Function operators are a special case of **function factories**, since they return functions. -```{r 10-01, fig.align='center',fig.dim="50%", fig.cap="Credits: 2001-2003 Michael P.Frank (https://slideplayer.com/slide/17666226/)",echo=FALSE} - -knitr::include_graphics("images/11-maths_example.png") -``` +- They are often used to wrap an existing function to provide additional capability, similar to python's **decorators**. +```{r 11-01_0} +chatty <- function(f) { + force(f) + + function(x, ...) { + res <- f(x, ...) + cat("Processing ", x, "\n", sep = "") + res + } +} +f <- function(x) x ^ 2 +s <- c(3, 2, 1) -- A **function operator** is a function that takes one (or more) functions as input and returns a function as output +purrr::map_dbl(s, chatty(f)) -- Function operators are closely related to **function factories**, infact are a particular case which uses a function as an input. +``` +## Existing function operators -These are the two libraries to use for finding function operator examples: +Two function operator examples are `purrr:safely()` and `memoise::memoise()`. These can be found in `purr` and `memoise`: ```{r 11-01_1} library(purrr) library(memoise) ``` -**What do they do?** +## purrr::safely {-} -Caching errors: +Capturing Errors: turns errors into data! - purrr::safely() -Caching computations: this function is an example of dynamic programming +```{r 11-02_0} +x <- list( + c(0.512, 0.165, 0.717), + c(0.064, 0.781, 0.427), + c(0.890, 0.785, 0.495), + "oops" +) +``` - memoise::memoise() +```{r 11-02_0b, eval=FALSE} +map_dbl(x, sum) +#> Error in .Primitive("sum")(..., na.rm = na.rm): invalid 'type' (character) of +#> argument +``` -### Other exsitsting function operators +```{r 11-02_0c} +# note use of map (not map_dbl), safely returns a lisst + +out <- map(x, safely(sum)) +str(transpose(out)) +``` + +## Other `purrr` function operators {-} + +<!--- ```{r 11-02, echo=FALSE,fig.align='center', fig.cap="Credits: www.nextptr.com"} knitr::include_graphics("images/11-function_operators.png") ``` +---> > purrr comes with three other function operators in a similar vein: @@ -65,12 +96,47 @@ knitr::include_graphics("images/11-function_operators.png") auto_browser(): automatically executes browser() inside the function when there’s an error. +## memoise::memoise {-} + +Caching computations: avoid repeated computations! + + +```{r 11-02_01} +slow_function <- function(x) { + Sys.sleep(1) + x * 10 * runif(1) +} +system.time(print(slow_function(1))) +system.time(print(slow_function(1))) +``` + +```{r 11-02_02} +fast_function <- memoise::memoise(slow_function) +system.time(print(fast_function(1))) +system.time(print(fast_function(1))) +``` + +> Be careful about memoising impure functions! + +## Exercise {-} +How does `safely()` work? +The source code looks like this: + +```{r 11-02_exp1} +safely +``` + +The real work is done in `capture_error` which is defined in the package **namespace**. We can access it with the `:::` operator. (Could also grab it from the function's environment.) + +```{r 11-02_exp2} +purrr:::capture_error +``` ## Case study: make your own function operator -```{r 11-03,eval=FALSE} +```{r 11-03_01,eval=FALSE} urls <- c( "adv-r" = "https://adv-r.hadley.nz", "r4ds" = "http://r4ds.had.co.nz/" @@ -84,7 +150,7 @@ walk2(urls, path, download.file, quiet = TRUE) Here we make a function operator that add a little delay in reading each page: -```{r 11-04} +```{r 11-03_02} delay_by <- function(f, amount) { force(f) force(amount) @@ -95,17 +161,47 @@ delay_by <- function(f, amount) { } } system.time(runif(100)) -#> user system elapsed -#> 0 0 0 + system.time(delay_by(runif, 0.1)(100)) -#> user system elapsed -#> 0.00 0.00 0.13 + +``` + + +And another to add a dot after nth invocation: + +```{r 11_03-04} +dot_every <- function(f, n) { + force(f) + force(n) + + i <- 0 + function(...) { + i <<- i + 1 + if (i %% n == 0) cat(".") + f(...) + } +} + +walk(1:100, dot_every(runif, 10)) + ``` -```{r 11-05, eval=FALSE} -walk2(urls, path, delay_by(download.file, 0.1), quiet = TRUE) +Can now use both of these function operators to express our desired result: + +```{r 11-03_05, eval=FALSE} +walk2( + urls, path, + download.file %>% dot_every(10) %>% delay_by(0.1), + quiet = TRUE +) ``` +## Exercise {-} + +2) Should you memoise file.download? Why or why not? + + + ## Meeting Videos ### Cohort 1