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:
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