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