From db83100328909590109dc791223149483b1452b4 Mon Sep 17 00:00:00 2001 From: Margie Hannum Date: Wed, 10 Apr 2019 10:06:38 -0400 Subject: [PATCH 01/12] ch7 ch8 notes --- advanced_r/ch7_oo_fieldguide_mh.Rmd | 90 ++++++ advanced_r/ch8_environments_mh.Rmd | 230 ++++++++++++++++ advanced_r/ch8_environments_mh.md | 293 ++++++++++++++++++++ esthers_notes ch7.Rmd | 412 ++++++++++++++++++++++++++++ 4 files changed, 1025 insertions(+) create mode 100644 advanced_r/ch7_oo_fieldguide_mh.Rmd create mode 100644 advanced_r/ch8_environments_mh.Rmd create mode 100644 advanced_r/ch8_environments_mh.md create mode 100644 esthers_notes ch7.Rmd diff --git a/advanced_r/ch7_oo_fieldguide_mh.Rmd b/advanced_r/ch7_oo_fieldguide_mh.Rmd new file mode 100644 index 0000000..d1cd33e --- /dev/null +++ b/advanced_r/ch7_oo_fieldguide_mh.Rmd @@ -0,0 +1,90 @@ +--- +title: "Chapter 7 Notes" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(pryr) +``` + +# [Chapter 7: OO field guide](http://adv-r.had.co.nz/OO-essentials.html) + +**Opening quiz** + +1. +```{r} +x <- 1 + +!is.object(x) +!isS4(x) +!is(x, "refClass") + +y <- data.frame(a = 1, b = 2) + +!is.object(y) +!isS4(y) +!is(y, "refClass") +``` + +2. +```{r} + +typeof(x) + +typeof(y) +``` + + +# S3 sections + +# S4 + +Didn't know about the special operator "@" for S4! + +Also didn't know about including explicit `library(methods)` when using S4. + +```{r} +library(stats4) +# From example(mle) +y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8) +nLL <- function(lambda) - sum(dpois(y, lambda, log = TRUE)) +fit <- mle(nLL, start = list(lambda = 5), nobs = length(y)) + +# An S4 object +isS4(fit) + +otype(fit) + +getGenerics() + +``` + +## Case Studies +From [S4 In Bioconductor](http://www.bioconductor.org/help/course-materials/2010/AdvancedR/S4InBioconductor.pdf) presentation. + +```{r} +library(Biobase) + +isVirtualClass("eSet") +isVirtualClass("ExpressionSet") + +getSlots("ExpressionSet") + +library(limma) +getSlots("MAList") + +library(IRanges) +is(new("CompressedIRangesList")) + +getSlots("RangesList") + +``` + + diff --git a/advanced_r/ch8_environments_mh.Rmd b/advanced_r/ch8_environments_mh.Rmd new file mode 100644 index 0000000..564ee7c --- /dev/null +++ b/advanced_r/ch8_environments_mh.Rmd @@ -0,0 +1,230 @@ +--- +title: "Chapter 8 Notes" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(pryr) +``` + +# [Chapter 8: Environments](http://adv-r.had.co.nz/Environments.html) + +**Opening quiz** + +1. Ways environment is different to a list: + +* elements in list don't need to be named, names in environments must be unique +* order doesn't matter in environment +* environments have parents (except the empty environment, i.e. the most ancient ancestor) (note the empty environment is the parent of the base environment) + +2. Parent of the global environment is the last package you loaded + +3. Enclosing environment: where the function was created (and where it looks for variables) + +4. How to determine environment from which a function was called? `parent.frame` +"The parent frame of a function evaluation is the environment in which the function was called. It is not necessarily numbered one less than the frame number of the current evaluation, nor is it the environment within which the function was defined. sys.parent returns the number of the parent frame if n is 1 (the default), the grandparent if n is 2, and so on. See also the ‘Note’." + +5. <- binds in current environment, <<- binds in parent of current environment + +## 8.1 Environment basics +Place where names and values match up! Environment contains the names IN that environment. + +Parent environments important for *lexical scoping*, i.e. if the name isn't in an environment, R will look up at the parent. + +This is why order of package loads can matter (masking!) + +```{r} +e <- new.env() + +parent.env(e) +ls(e) + +e$a <- 1 +e$b <- 2 +ls(e) + +# shows each object in environment so may be more useful than str() at times +ls.str(e) + +``` + +Can't remove things by setting them to NULL (like you can with a list). Have to use `rm()`. + +```{r} +e$a +e$a <- NULL +ls(e) +e$a +rm("a", envir = e) + +ls(e) + +plus <- function(x) { + function(y) x + y +} +plus_one <- plus(2) + + +plus_one(3) +plus_one + +``` + +### Exercises +1. In environment: can't remove items by setting them to NULL, elements need a name, order doesn't matter. + +2. If you don't supply an explicit environment, ls() and rm() look in the globalenv(). Use parameter `envir = ` to specify where to start to look. Lexical scoping will hapen unless you set `inherits = FALSE`. + +3. Got answer from Jenny Bryan: + +It is a recursive function because it uses it's own function inside! +Goes up and up the family tree until it reaches `emptyenv()`, at which point it stops +```{r} +j_search <- function(env = globalenv()) { + if (identical(env, emptyenv())) { + return(invisible(NULL)) + } else { + return(c(environmentName(env), j_search(parent.env(env)))) + } + } +j_search() + +j_search(e) +# environmentName returns character string +# parent.env returns the enclosing environment of it's first argument + +``` + +## 8.2 Recursing over environments + +```{r} + +where("e") + +where("mean") + +library(MASS) +library(dplyr) + +where("select") +parent.env(.GlobalEnv) +``` + +### Exercises + +1. Modify `where()` to find all envrionments taht contain a binding for `name`. + +p.s. blog article which answers the question [What on earth is binding?](https://colinfay.me/ractivebinfing/) +```{r} +where_m <- function(name, env = parent.frame()) { + if (identical(env, emptyenv())) { + # Base case + return(invisible(NULL)) + + } else if (exists(name, envir = env, inherits = FALSE)) { + # Success case + return(c(environmentName(env), where_m(name, parent.env(env)))) + + } else { + # Recursive case + where_m(name, parent.env(env)) + + } +} + +median <- "test" +where_m("median") + +rm("median") +where_m("median") +``` + +2. Version of `get()` + +## 8.3 Function Environments +enclosing, binding, execution, and calling + +*Think of it as casting a spell* + +### Enclosing +i.e. environment where function was created. + +```{r} +environment(where) +environment(where_m) +``` + +*Distinction between the binding environment and the enclosing environment is important for package namespaces* + +Every package has both package and namespace envrionments. +Internal functions that aren't exported as part of the package environment are kept in the namespace environment. Package environment is the public face. + +### Execution + +The below returns the same thing twice because of "Fresh Start Principle" +```{r} +g <- function(x) { + if (!exists("a", inherits = FALSE)) { + message("Defining a") + a <- 1 + } else { + a <- a + 1 + } + a +} +g(10) +g(10) +``` + +### Calling +```{r} +h <- function() { + x <- 10 + function() { + x + } +} +i <- h() +x <- 20 +i() +``` + +### Exercises + +1. Four environments associated with a function: + +* Enclosing: where function was created +* Binding: where function named +* Execution: ephemeral environment that stores variables created while executing a function +* Calling: Where the function is called + +2. Draw diagram of: + +```{r} +f1 <- function(x1) { + f2 <- function(x2) { + f3 <- function(x3) { + x1 + x2 + x3 + } + f3(3) + } + f2(2) +} +f1(1) + +``` + +## Binding names to values + +Assignment: bind name in an environment +Scoping: look for value associated with name in environment +Lexical scoping: scope in environment recursively up parent environments until you find value associated with name + + diff --git a/advanced_r/ch8_environments_mh.md b/advanced_r/ch8_environments_mh.md new file mode 100644 index 0000000..b0712d7 --- /dev/null +++ b/advanced_r/ch8_environments_mh.md @@ -0,0 +1,293 @@ +Chapter 8 Notes +================ +M Hannum +2019-03-29 + +- [[Chapter 8: Environments](http://adv-r.had.co.nz/Environments.html)](#chapter-8-environments) + - [8.1 Environment basics](#environment-basics) + - [8.2 Recursing over environments](#recursing-over-environments) + - [8.3 Function Environments](#function-environments) + - [Binding names to values](#binding-names-to-values) + +[Chapter 8: Environments](http://adv-r.had.co.nz/Environments.html) +=================================================================== + +**Opening quiz** + +1. Ways environment is different to a list: + +- elements in list don't need to be named, names in environments must be unique +- order doesn't matter in environment +- environments have parents (except the empty environment, i.e. the most ancient ancestor) (note the empty environment is the parent of the base environment) + +1. Parent of the global environment is the last package you loaded + +2. Enclosing environment: where the function was created (and where it looks for variables) + +3. How to determine environment from which a function was called? `parent.frame` "The parent frame of a function evaluation is the environment in which the function was called. It is not necessarily numbered one less than the frame number of the current evaluation, nor is it the environment within which the function was defined. sys.parent returns the number of the parent frame if n is 1 (the default), the grandparent if n is 2, and so on. See also the ‘Note’." + +4. <- binds in current environment, <<- binds in parent of current environment + +8.1 Environment basics +---------------------- + +Place where names and values match up! Environment contains the names IN that environment. + +Parent environments important for *lexical scoping*, i.e. if the name isn't in an environment, R will look up at the parent. + +This is why order of package loads can matter (masking!) + +``` r +e <- new.env() + +parent.env(e) +``` + + ## + +``` r +ls(e) +``` + + ## character(0) + +``` r +e$a <- 1 +e$b <- 2 +ls(e) +``` + + ## [1] "a" "b" + +``` r +# shows each object in environment so may be more useful than str() at times +ls.str(e) +``` + + ## a : num 1 + ## b : num 2 + +Can't remove things by setting them to NULL (like you can with a list). Have to use `rm()`. + +``` r +e$a +``` + + ## [1] 1 + +``` r +e$a <- NULL +ls(e) +``` + + ## [1] "a" "b" + +``` r +e$a +``` + + ## NULL + +``` r +rm("a", envir = e) + +ls(e) +``` + + ## [1] "b" + +### Exercises + +1. In environment: can't remove items by setting them to NULL, elements need a name, order doesn't matter. + +2. If you don't supply an explicit environment, ls() and rm() look in the globalenv(). Use parameter `envir =` to specify where to start to look. Lexical scoping will hapen unless you set `inherits = FALSE`. + +3. Got answer from Jenny Bryan: + +It is a recursive function because it uses it's own function inside! Goes up and up the family tree until it reaches `emptyenv()`, at which point it stops + +``` r +j_search <- function(env = globalenv()) { + if (identical(env, emptyenv())) { + return(invisible(NULL)) + } else { + return(c(environmentName(env), j_search(parent.env(env)))) + } + } +j_search() +``` + + ## [1] "R_GlobalEnv" "package:pryr" "package:stats" + ## [4] "package:graphics" "package:grDevices" "package:utils" + ## [7] "package:datasets" "package:methods" "Autoloads" + ## [10] "base" + +``` r +j_search(e) +``` + + ## [1] "" "R_GlobalEnv" "package:pryr" + ## [4] "package:stats" "package:graphics" "package:grDevices" + ## [7] "package:utils" "package:datasets" "package:methods" + ## [10] "Autoloads" "base" + +``` r +# environmentName returns character string +# parent.env returns the enclosing environment of it's first argument +``` + +8.2 Recursing over environments +------------------------------- + +``` r +where("e") +``` + + ## + +``` r +where("mean") +``` + + ## + +### Exercises + +1. Modify `where()` to find all envrionments taht contain a binding for `name`. + +p.s. blog article which answers the question [What on earth is binding?](https://colinfay.me/ractivebinfing/) + +``` r +where_m <- function(name, env = parent.frame()) { + if (identical(env, emptyenv())) { + # Base case + return(invisible(NULL)) + + } else if (exists(name, envir = env, inherits = FALSE)) { + # Success case + return(c(environmentName(env), where_m(name, parent.env(env)))) + + } else { + # Recursive case + where_m(name, parent.env(env)) + + } +} + +median <- "test" +where_m("median") +``` + + ## [1] "R_GlobalEnv" "package:stats" + +``` r +rm("median") +where_m("median") +``` + + ## [1] "package:stats" + +1. Version of `get()` + +8.3 Function Environments +------------------------- + +enclosing, binding, execution, and calling + +*Think of it as casting a spell* + +### Enclosing + +i.e. environment where function was created. + +``` r +environment(where) +``` + + ## + +``` r +environment(where_m) +``` + + ## + +*Distinction between the binding environment and the enclosing environment is important for package namespaces* + +Every package has both package and namespace envrionments. Internal functions that aren't exported as part of the package environment are kept in the namespace environment. Package environment is the public face. + +### Execution + +The below returns the same thing twice because of "Fresh Start Principle" + +``` r +g <- function(x) { + if (!exists("a", inherits = FALSE)) { + message("Defining a") + a <- 1 + } else { + a <- a + 1 + } + a +} +g(10) +``` + + ## Defining a + + ## [1] 1 + +``` r +g(10) +``` + + ## Defining a + + ## [1] 1 + +### Calling + +``` r +h <- function() { + x <- 10 + function() { + x + } +} +i <- h() +x <- 20 +i() +``` + + ## [1] 10 + +### Exercises + +1. Four environments associated with a function: + +- Enclosing: where function was created +- Binding: where function named +- Execution: ephemeral environment that stores variables created while executing a function +- Calling: Where the function is called + +1. Draw diagram of: + +``` r +f1 <- function(x1) { + f2 <- function(x2) { + f3 <- function(x3) { + x1 + x2 + x3 + } + f3(3) + } + f2(2) +} +f1(1) +``` + + ## [1] 6 + +Binding names to values +----------------------- + +Assignment: bind name in an environment Scoping: look for value associated with name in environment Lexical scoping: scope in environment recursively up parent environments until you find value associated with name diff --git a/esthers_notes ch7.Rmd b/esthers_notes ch7.Rmd new file mode 100644 index 0000000..06db077 --- /dev/null +++ b/esthers_notes ch7.Rmd @@ -0,0 +1,412 @@ +--- +title: "Chapter 7 Notes" +author: "Esther Drill" +date: "March 8, 2019" +output: + html_document: + toc: true + toc_float: true +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) +library(tidyverse) +library(pryr) +library(stats4) +``` + +# Intro + +Not particularly comprehensible, moving on to the rest of the chapter, which +I hope will prove easier to fathom. + + +# Quiz + +#### How do you tell what OO system (base, S3, S4, or RC) an object is associated with? + +No idea. But hopeful I will know by the end of the chapter + +*Correct answer: process of elimination. Not convinced this is the best way...* + +#### 2. How do you determine the base type (like integer or list) of an object? + +My idea: +is.integer(x) +is.list(x) + +WRONG! + +*Correct answer:* **typeof(x)* *** + + +#### 3. What is a generic function? + +I think I know but not sure how to describe it. + +*Correct answer: A generic function calls specific methods depending on the class of its inputs. In S3 and S4 object systems, methods belong to generic functions,not classes like in other programming languages.* + +This clearly seems most useful for people with background in programming... + + +#### 4. What are the main differences between S3 and S4? What are the main differences between S4 & RC? + +Well, S4 is more formal than S3, and also has multiple dispatch, but don't really +know what that means. + +Don't understand RC at all. + +*Correct answer: S4 is more formal than S3, and supports multiple inheritance and multiple dispatch. RC objects have reference semantics, and methods belong to classes, not functions.* + +# 7.1: Base types + +* Underlying every R ojbect is a C struct, which includes a **type**. +* Most common base types: **atomic vectors** and **lists**. +* Also: **functions** and **environments**, as well as **names**, **calls**, and **promsises**. +* Determine an object's base type with **typeof()**. +* Names of base types are inconsistent throughout R and don't always match up with "is" functions. + +```{r base_types_1, eval = T} +# The type of a function is "closure" +# I always wondered what closure meant in R. +f <- function() {} +typeof(f) +is.function(f) +``` + +```{r base_types_2, eval = T} +# The type of a primitive function is "builtin" +typeof(sum) +is.function(sum) +``` + +* S3 objects can be built on top of any base type +* S4 objects use a special base type +* RC objects are a combo of S4 and enviornments (another base type) +* To see if an object is a pure base type, check that **is.object(x)** returns FALSE + +# 7.2: S3 + +* Only OO system used in base R +* Most commonly used system in CRAN packages + +## 7.2.1: Recognizing objects, generic functions, and methods + +* No simple way to test if an object is an S3 object in base R. +* Closest you can come is **is.object(x)** & **!isS4(x)** +* But you can use **pryr::otype()** like so: + +```{r S3_1, eval = T} +library(pryr) +df <- data.frame(x = 1:10, y = letters[1:10]) +head(df) +otype(df) # A data frame is an s3 class +otype(df$x) # A numeric vector isn't +otype(df$y) # A factor is +``` + +* In S3 methods belong to functions, called **generic functions** +* S3 methods do not belong to objects or classes. (Different from most other programming languages.) +* To determine if a function is an S3 generic, inspect source code for a call to **UseMethod()**. +* **UseMethod()** is the function that figures out the correct method to call: **method dispatch** +* **pryr::ftype()** describes object systems associated with function + +```{r S3_2, eval = T} +mean +ftype(mean) +``` + +* S3 generics implemented in C don't call **UseMethod()**. + +```{r S3_3, eval = T} +sum +ftype(sum) +``` + +* Job of an S3 generic is to call the right S3 method. +* S3 methods' names look like **generic.class()** +* This can be confusing when function names use **.**. This is why style guides suggest to use **_** in function names instead. + +```{r S3_4, eval = T} +ftype(t.data.frame) # data frame method for t() +ftype(t.test) # generic function for t tests +``` + +* You can see all methods that belong to a generic with **methods()** +* S3 methods not defined in base package will not be visible. Can use **getS3method()** to read source code. +*Would like to see an example of this.* +* No way to list all S3 classes. + +## 7.2.2: Defining classes and creating objects + +* S3 simple and ad hoc system. +* To make an object an instance of a class, take an existing base object and set the class attribute. +* S3 objects usually built on top of lists, or atomic vectors with attributes. + +```{r S3_classes_1, eval = T} +# Create and assign class in one step +foo <- structure(list(), class = "foo") +# Create, then set class +foo <- list() +class(foo) <- "foo" +``` + +* Determine class of any object, and see if object inherits from a specific class: + +```{r S3_classes_2, eval = T} +class(foo) +inherits(foo, "foo") +``` + +* Class of an object can be a vector, with behavior from most to least specific. +* Class of **glm()** object is **c("glm","lm")** meaning that generalized linear models inherit behavior from linear models. +* Most S3 classes provide a constructor function: +```{r S3_classes_3, eval = F} +foo <- function(x) { + if (!is.numeric(x)) stop("X must be numeric") + structure(list(x), class = "foo") +``` +* S3 has no checks for correctness. +## 7.2.3: Creating new methods and generics +* To add a new generic: +```{r S3_methods_1, eval = T} +f<- function(x) UseMethod("f") +f.a <- function(x) "Class a" +a <- structure(list(), class = "a") +class(a) +f(a) +mean.a <- function(x) "a" +mean(a) +``` +* No check to make sure that the method returns the class compatible with the generic. +## 7.2.4: Method dispatch +* **UseMethod()** creates a vector of function names, and looks for each in turn: +```{r S3_methods_2, eval = T} +f<- function(x) UseMethod("f") +f.a <- function(x) "Class a" +f.default <- function(x) "Unknown class" +f(structure(list(), class = "a")) +# No method for b class, so uses method for a class +f(structure(list(), class = c("b","a"))) +# No method for c class, so falls back to default +f(structure(list(), class = "c")) +``` +## Exercises +#### 1. Read the source code for t() and t.test() and confirm that t.test() is an S3 generic and not an S3 method. What happens if you create an object with class test and call t() with it? +```{r, ex_1 = T, error=TRUE} +t +ftype(t) +t.test +ftype(t.test) +test <- structure(list(), class = "test") +t(test) +``` +#### 2. What classes have a method for the **Math** group generic in base R? Read the source code. How do the methods work? +From **?groupGeneric** details, we see the following classes have a method for the **Math** group: +* factor +* data.frame +* difftime +* Date +```{r, ex_2 = T} +Math.factor +Math.data.frame +``` +#### 3. R has two classes for representing date time data, **POSIXct** and **POSIXlt**, which both inherit from **POSIXt**. Which generics have different behaviours for the two classes? Which generics share the same behaviour? +```{r, ex_3 = T} +methods(class = "POSIXct") +methods(class = "POSIXlt") +methods(class = "POSIXt") +``` +So I think the idea is that the methods listed for **POSIXt** are shared by **POSIXct** and **POSIXlt**, but the methods listed for **POSIXct** and **POSIXlt** only have different behaviors for the two classes. +#### 4. Which base generic has the greatest number of defined methods? +```{r, ex_4 = T} +## From Chapter 6 +objs <- mget(ls("package:base"), inherits = TRUE) +funs <- Filter(is.function, objs) +funs_names <- names(funs) +funs_names[100:120] +generic_names <- str_split_fixed(funs_names, fixed("."), 2) +generic_names[100:120,] +funs_numbers <- sort(table(generic_names[,1]),decreasing = TRUE) +head(funs_numbers) +ftype(as) +head(funs_names[grep("as.",funs_names, fixed=TRUE)],20) +``` +#### 5. Run the code to figure out what is going on: +```{r, ex_5 = T} +y <- 1 +g <- function(x) { + y <- 2 + UseMethod("g") +} +g.numeric <- function(x) y +g(10) +h <- function(x) { + x <- 10 + UseMethod("h") +} +h.character <- function(x) paste("char", x) +h.numeric <- function(x) paste("num", x) +h("a") +``` +#### 6. Internal generics don't dispatch on the implicit class of base types. Carefully read ?"internal generic" to determine why the length of **f** and **g** is different in the example below. What function helps distinguish between the behaviour of **f** and **g**? +```{r, ex_6 = T} +f <- function() 1 +g <- function() 2 +class(g) <- "function" +class(f) +class(g) +length.function <- function(x) "function" +length(f) +length(g) +``` +# 7.3: S4 +* Like S3, but with formality and rigor +* Classes have formal definitions which describe their fields and inheritance structures (parent classes) +* Method dispatch can be based on multiple arguments, not just one +* Special operator, @, for extracting slots (aka fileds) from S4 object +* No S4 classes in commonly used base packages +* Examples of packages that make good use of S4 objects are not so easy to find + - lme4 + - iRanges + - ExpressionSet class (for differential gene analysis) + - stats4 package provides some S4 classes and methods associated with mle +## 7.3.1: Recognizing objects, generic functions, and methods +* How to create +```{r S4_objects, eval = T} +library(stats4) +y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8) +nLL <- function(lambda) - sum(dpois(y, lambda, log=TRUE)) +fit <- mle(nLL, start = list(lambda = 5), nobs = length(y)) +str(fit) +isS4(fit) +otype(fit) +str(nobs) +isS4(nobs) +ftype(nobs) +# Retrieve an S4 method +mle_nobs <- method_from_call(nobs(fit)) +str(mle_nobs) +isS4(mle_nobs) +ftype(nobs) +# List all classes that an object inherits from +is(fit) +``` +There are various ways to get lists of all S4 generics (pp 112-113) +* **getGenerics()** lists all S4 generics +* **getClasses()** lists all S4 classes (in currently used packages) + - This list includes shim classes for S3 classes and base types + - From Wikipedia: In computer programming, a **shim** is a library that transparently intercepts API calls and changes the arguments passed, handles the operation itself or redirects the operation elsewhere +* **showMethods()** lists all S4 methods, and can restrict selection by **generic** or **class** or both +```{r S4_objects_2, eval = T, error = T} +getClasses(where = "package:methods") +getClasses(where = "package:stats4") +``` +## 7.3.2: Defining classes and creating objects +* As opposed to S3, where you can turn any object into an object of a particular class by setting class attribute, +S4 is much stricter. +* S4 class has 3 properties: + - A name in UpperCamelCase + - A names list of **slots**, which defines slot names and permitted classes. + - A string giving the class it inherits from (or in S4-speak, **contains**) + - See **?setClass** for more details on optional properties and the special class **ANY*** +* This code creates a Person class with fields name and age, and an Employee class that inherits from Person. +```{r S4_class_1, eval = T} +setClass("Person", + slots = list(name = "character", age = "numeric")) +setClass("Employee", + slots = list(boss = "Person"), + contains = "Person") +alice <- new("Person", name = "Alice", age = 40) +john <- new("Employee", name = "John", age = 20, boss = alice) +``` +If the S4 vlass comes with a constructor function with the same name as the class, use that instead of calling **new()** directly. +```{r S4_class_2, eval = T} +str(john) +## To access slots: +alice@age +slot(john, "boss") +``` +If an S4 object contains (inherits from) an S3 class or a base type, it will have a special **.Data** slot which contains the underlying object, like so: +```{r S4_class_3, eval = T} +setClass("RangedNumeric", + contains = "numeric", + slots = list(min = "numeric", max = "numeric")) +rn <- new("RangedNumeric", 1:10, min = 1, max = 10) +rn@min +rn@.Data +``` +If you modify a class, make sure you also recreate any objects of that class. Otherwise, objects will be invalid. +## 7.3.3: Creating new methods and generics +* **setGeneric()** creates a new generic or convers an existing function into a generic +* **setMethod()** takes the name of the generic, the classes the method should be associated with, and a function that implements the method. +```{r S4_method_1, eval = T} +setGeneric("union") +setMethod("union", + c(x = "data.frame", y = "data.frame"), + function(x, y) { + unique(rbind(x, y)) + }) +``` +To create a new generic from scratch, supply a function that calls **standardGeneric()**, which is the S4 equivalent to **UseMethod()** +```{r S4_method_2, eval = T} +setGeneric("myGeneric", function(x) { + standardGeneric("myGeneric") +}) +``` + +## 7.3.4: Method dispatch + +* If S4 generic dispatches on a single class with a single parent, then S4 method dispatch is the same as S3 dispatch. + - S4 uses special class **ANY** to match any class + - S4 uses "missing" to match a missing argument. +* Method dispatch becomes more complicated in you dispatch on multiple arguments, or if your classes use multiple inheritance. + +To find out which method gets called give the specification of a generic call: + +```{r S4_method_3, eval = T} +selectMethod("nobs", list("mle")) +method_from_call(nobs(fit)) +``` + +## Exercises + +#### 1. Which S4 generic has the most methods defined for it? Which S4 class has the mosst methods associated with it? + + +```{r, S4_ex_1 = T, error=TRUE} +getGenerics(where = "package:methods") +genericsS4 <- getGenerics(where = search()) +str(genericsS4) +genericsS4_names <- genericsS4@.Data +### find methods defined for every S4 generic +generic_methods <- lapply(genericsS4_names, function(x) {methods(x)}) +### get # of methods defined for every S4 generic +length_gmethods <- unlist(lapply(generic_methods, length)) +### find the generic with the max length +generic_most_methods <- generic_methods[which.max(length_gmethods)] +ftype(summary) +``` + +Theoretically, to find the S4 class with the most associated methods, you would have to take the inherited classes into account and see how many methods were associated with the inherited classes as well. + +#### 2. What happens if you define a new S4 class that doesn't "contain" an existing class? (Hint: read about virtual classes in **?classes**.) + +I read about virtual classes in **?Classes** and it says, +"...if the class does not inherit from a type, objects form the class will have type "S4"." + +#### 3. What happens if you pass an S4 object to an S3 generic? What happens if you pass an S3 object to an S4 generic? (Hint: read **?setOldClass** for the second case.) + +```{r, S4_ex_3 = T, error=TRUE} +otype(alice) +mean(alice) +data.frame(alice) +``` + +# RC + +* Newest OO system in R +* RC methods belong to objects, not functions +* RC objects are changeable +* RC objects behave more like objects do in most other programming languages +* Special S4 class that wraps around an environment. \ No newline at end of file From 6faf2c67478212ea4db3ebc6919bd265b9d52b96 Mon Sep 17 00:00:00 2001 From: Margie Hannum Date: Fri, 17 May 2019 12:37:21 -0400 Subject: [PATCH 02/12] ch 9 and 10 notes --- advanced_r/ch10_functional_programming_mh.Rmd | 28 +++++ advanced_r/ch9_debugging_mh.Rmd | 115 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 advanced_r/ch10_functional_programming_mh.Rmd create mode 100644 advanced_r/ch9_debugging_mh.Rmd diff --git a/advanced_r/ch10_functional_programming_mh.Rmd b/advanced_r/ch10_functional_programming_mh.Rmd new file mode 100644 index 0000000..d89459a --- /dev/null +++ b/advanced_r/ch10_functional_programming_mh.Rmd @@ -0,0 +1,28 @@ +--- +title: "Chapter 10 Notes" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(pryr) +``` + +# [Chapter 10: Functional Programming](http://adv-r.had.co.nz/Functional-programming.html) + +1. Functional = Take function as an argument + +2. "Anonymous function" which I knew after experience but never thought of as a concept + +Exercises +```{r} +cv_list <- lapply(mtcars, function(x) sd(x)/mean(x)*100) +``` + + diff --git a/advanced_r/ch9_debugging_mh.Rmd b/advanced_r/ch9_debugging_mh.Rmd new file mode 100644 index 0000000..7a2feb5 --- /dev/null +++ b/advanced_r/ch9_debugging_mh.Rmd @@ -0,0 +1,115 @@ +--- +title: "Chapter 8 Notes" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(pryr) +``` + +# [Chapter 9: Debugging](http://adv-r.had.co.nz/Exceptions-Debugging.html) + +Defensive programming principles: + +* Check inputs are correct +* Avoid non-standard evaluation +* Avoid functions that can return different types of output + +**Opening quiz** + +1. How can you find out where an error occurred? + +Look at console results or use traceback. + +2. How does `browser()` do? + +?? + +3. What function do you use to ignore errors in a block of code? + +`tryCatch()`. Can also nest it within functions. + +4. Why might you want to create an error with a custom S3 class? + +?? + +```{r} +f <- function(a) g(a) +g <- function(b) h(b) +h <- function(c) i(c) +i <- function(d) "a" + d +f(10) +``` + +Exercises: + +What is advantage of withCallingHandlers()? +```{r} +condition <- function(subclass, message, call = sys.call(-1), ...) { + structure( + class = c(subclass, "condition"), + list(message = message, call = call), + ... + ) +} +is.condition <- function(x) inherits(x, "condition") +e <- condition(c("my_error", "error"), "This is an error") +message2error <- function(code) { + withCallingHandlers(code, message = function(e) stop(e)) +} +message2error2 <- function(code) { + tryCatch(code, message = function(e) stop(e)) +} + +message2error(stop(e)) +traceback() +message2error2(stop(e)) +traceback() +``` +Fewer lines in 1st traceback? + +# Defensive programming + +1. Strict input: look into `assertthat::stopifnot()` + +2. Avoid functions that use non-standard evaluation (e.g. subset, transform, with) + +3. Avoid functions that return different types of output depening on input. Specifically: `sapply()` and `[` + +## Exercises +The goal of the col_means() function defined below is to compute the means of all numeric columns in a data frame. +```{r} +col_means <- function(df) { + numeric <- sapply(df, is.numeric) + numeric_cols <- df[, numeric] + + data.frame(lapply(numeric_cols, mean)) +} +``` + + +However, the function is not robust to unusual inputs. Look at the following results, decide which ones are incorrect, and modify col_means() to be more robust. (Hint: there are two function calls in col_means() that are particularly prone to problems.) + +```{r} +col_means(mtcars) +col_means(mtcars[, 0]) +col_means(mtcars[0, ]) +col_means(mtcars[, "mpg", drop = F]) +col_means(1:10) +col_means(as.matrix(mtcars)) +col_means(as.list(mtcars)) +mtcars +mtcars2 <- mtcars +mtcars2[-1] <- lapply(mtcars2[-1], as.character) +col_means(mtcars2) +``` + + + From d7e17c0706e9e3bf5c2754eb9f5548ff772f5b93 Mon Sep 17 00:00:00 2001 From: margarethannum Date: Thu, 13 Jun 2019 12:44:33 -0400 Subject: [PATCH 03/12] Create ch11_functional_programming_neweditionch9_mh.Rmd --- ...unctional_programming_neweditionch9_mh.Rmd | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd diff --git a/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd new file mode 100644 index 0000000..2cddd32 --- /dev/null +++ b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd @@ -0,0 +1,45 @@ +--- +title: "Chapter 11 Notes" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(pryr) +``` + +# [Chapter 11: Functionals](https://adv-r.hadley.nz/functionals.html) + +*** Actually for this chapter we used the 2nd edition, which is chapter 9 in the second edition, since it uses functions from the purrr package. + + +## Predicate functionals + +I had never seen these! "`some(.x, .p)` returns `TRUE` when it sees the first `TRUE`, and `every()` returns `FALSE` when it sees the first `FALSE`. + +`detect_index` also seems super useful (returns location of the first match) + +`keep(.x, .p)` keeps all matching elements + +9.6.3 Exercises +1. Why isn't is.na() a predicate function? What base R function is closest to being a predicate version of is.na()? + +```{r} +x <- c(1, 2, NA) + +is.na(x) + +anyNA(x) +``` + +is.na() is not a predicate function becuase it returns more than 1 TRUE or FALSE. `anyNA` is close to a predicate function because it returns a single value, saying if there are any NA in the vector. + + + + From 54fa9111fd0242fa7d169a331ad5147e24566d73 Mon Sep 17 00:00:00 2001 From: Hannum Date: Wed, 3 Jul 2019 14:04:21 -0400 Subject: [PATCH 04/12] A commit from my local computer --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a4e217..3336ed7 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,5 @@ book-club A collection of notes from going through different statistical and programming texts. First on the docket is [**Advanced R**](http://adv-r.had.co.nz/) by Hadley Wickham. +"test" +Test line From 935178c9470d933107fb1d21ff152a45e92577d2 Mon Sep 17 00:00:00 2001 From: Hannum Date: Wed, 3 Jul 2019 14:10:00 -0400 Subject: [PATCH 05/12] All hail Jenny Bryan --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3336ed7..542b331 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,4 @@ A collection of notes from going through different statistical and programming t First on the docket is [**Advanced R**](http://adv-r.had.co.nz/) by Hadley Wickham. "test" Test line +Another test line From a56c68dfff40e4f408157c8e0f3c5c602d488a7f Mon Sep 17 00:00:00 2001 From: Hannum Date: Wed, 3 Jul 2019 14:14:20 -0400 Subject: [PATCH 06/12] local commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 542b331..08f60ac 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,4 @@ First on the docket is [**Advanced R**](http://adv-r.had.co.nz/) by Hadley Wickh "test" Test line Another test line +adding one more From 061562e5beecb468aa4c0b33eaa4b10f65c1866a Mon Sep 17 00:00:00 2001 From: Margaret Date: Wed, 3 Jul 2019 16:36:27 -0400 Subject: [PATCH 07/12] A commit from local again --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 08f60ac..41476e7 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ First on the docket is [**Advanced R**](http://adv-r.had.co.nz/) by Hadley Wickh Test line Another test line adding one more +adding a line again From 96126d2be85281f429e232788917d5ca1cbd863e Mon Sep 17 00:00:00 2001 From: Margaret Hannum Date: Fri, 12 Jul 2019 12:32:08 -0400 Subject: [PATCH 08/12] test push --- advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd index 2cddd32..6fe8c07 100644 --- a/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd +++ b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd @@ -40,6 +40,6 @@ anyNA(x) is.na() is not a predicate function becuase it returns more than 1 TRUE or FALSE. `anyNA` is close to a predicate function because it returns a single value, saying if there are any NA in the vector. - +Test change From 76d758f7933d5603339e47b2f227c024ff056752 Mon Sep 17 00:00:00 2001 From: Margaret Hannum Date: Wed, 11 Dec 2019 13:50:52 -0500 Subject: [PATCH 09/12] metaprogramming big picture notes --- ...unctional_programming_neweditionch9_mh.Rmd | 65 +++++-- .../ch17_metaprogramming_big_picture.Rmd | 180 ++++++++++++++++++ 2 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 advanced_r/ch17_metaprogramming_big_picture.Rmd diff --git a/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd index 6fe8c07..ac7fafb 100644 --- a/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd +++ b/advanced_r/ch11_functional_programming_neweditionch9_mh.Rmd @@ -1,5 +1,5 @@ --- -title: "Chapter 11 Notes" +title: "Section IV Metaprogramming" author: "M Hannum" date: "`r Sys.Date()`" output: @@ -11,35 +11,70 @@ output: ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) -library(pryr) +library(rlang) +library(lobstr) ``` -# [Chapter 11: Functionals](https://adv-r.hadley.nz/functionals.html) +# [Section IV Metaprogramming Chapter 17: Big Picture](https://adv-r.hadley.nz/metaprogramming.html) -*** Actually for this chapter we used the 2nd edition, which is chapter 9 in the second edition, since it uses functions from the purrr package. +*Metaprogramming* ... "code is data" so you can do cool things with it. "hardest topic in this book" +*Non-standard evaluation* ... -## Predicate functionals +## Code is data -I had never seen these! "`some(.x, .p)` returns `TRUE` when it sees the first `TRUE`, and `every()` returns `FALSE` when it sees the first `FALSE`. +```{r} +expr(10 + 100) + +``` + + +vs `enexpr()` -`detect_index` also seems super useful (returns location of the first match) -`keep(.x, .p)` keeps all matching elements +"Captured expressions behave like lists" - +I've been using this without really knowing what I'm doing! Trial and error using `[[` and `$`. -9.6.3 Exercises -1. Why isn't is.na() a predicate function? What base R function is closest to being a predicate version of is.na()? +## Code is a tree + +*Abstract syntax tree* i.e. AST ```{r} -x <- c(1, 2, NA) +lobstr::ast(f1(f2(a, b), f3(1, f4(2)))) +#> █─f1 +#> ├─█─f2 +#> │ ├─a +#> │ └─b +#> └─█─f3 +#> ├─1 +#> └─█─f4 +#> └─2 +``` + +## Code can generate code + +Using `rlang::call2() to create new trees from components. Function, then arguments. -is.na(x) +(** Similar to `do.call`)? Except it doesn't evaluate? + +```{r} +?do.call + +?call2 +``` + +```{r} +call2("f", 1, 2, 3) +#> f(1, 2, 3) +call2("+", 1, call2("*", 2, 3)) +#> 1 + 2 * 3 + +call2("+", 1, 2) +do.call("+", args = list(1, 2)) -anyNA(x) ``` -is.na() is not a predicate function becuase it returns more than 1 TRUE or FALSE. `anyNA` is close to a predicate function because it returns a single value, saying if there are any NA in the vector. -Test change + diff --git a/advanced_r/ch17_metaprogramming_big_picture.Rmd b/advanced_r/ch17_metaprogramming_big_picture.Rmd new file mode 100644 index 0000000..af24160 --- /dev/null +++ b/advanced_r/ch17_metaprogramming_big_picture.Rmd @@ -0,0 +1,180 @@ +--- +title: "Section IV Metaprogramming" +author: "M Hannum" +date: "`r Sys.Date()`" +output: + github_document: + toc: true + toc_depth: 2 +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) + +library(rlang) +library(lobstr) +``` + +# [Section IV Metaprogramming Chapter 17: Big Picture](https://adv-r.hadley.nz/metaprogramming.html) + +*Metaprogramming* ... "code is data" so you can do cool things with it. "hardest topic in this book" + +*Non-standard evaluation* ... + +## Code is data + +```{r} +expr(10 + 100) + +enexpr(x) +``` + + +vs `enexpr()` + + +"Captured expressions behave like lists" - +I've been using this without really knowing what I'm doing! Trial and error using `[[` and `$`. + +## Code is a tree + +*Abstract syntax tree* i.e. AST + +```{r} +lobstr::ast(f1(f2(a, b), f3(1, f4(2)))) +#> █─f1 +#> ├─█─f2 +#> │ ├─a +#> │ └─b +#> └─█─f3 +#> ├─1 +#> └─█─f4 +#> └─2 +``` + +## Code can generate code + +Using `rlang::call2() to create new trees from components. Function, then arguments. + +(** Similar to `do.call`)? Except it doesn't evaluate? + +```{r} +?do.call + +?call2 +``` + +```{r} +call2("f", 1, 2, 3) +#> f(1, 2, 3) +call2("+", 1, call2("*", 2, 3)) +#> 1 + 2 * 3 + + +do.call("+", args = list(1, 2)) + +call2("+", 1, call2("*", 2, 3)) + +call2("@~~@", x, y) + + +`+`(1, `*`(2, 3)) +``` + +```{r} +xx <- expr(x + x) +yy <- expr(y + y) + +xx + +expr(!!xx / !!yy) +expr(xx / yy) + +expr(!!enexpr(xx)/!!enexpr(yy)) +#> (x + x)/(y + y) + +tt <- +``` + + +"Avoiding paste() when generateing R code" - thoughts? + +## Evaluation runs code + +```{r} +eval(call2("+", 1, 2)) + +# set environment within eval +eval(expr(x + y), env(x = 1, y = 2)) + +# or set the environment outside the eval and eval looks for it +x <- 10 +y <- 100 +eval(expr(x + y)) +``` + +-- What is a "data mask" + +## Customizing evaluation with functions +Can do things like set the caller environment i.e. bind names to functions. + +## Customising evaluation with data +*** Looking for variables in a data frame instead of an environment + +```{r} +df <- data.frame(x = 1:5, y = sample(5)) + +eval_tidy(expr(x + y), df) + +with2 <- function(df, expr) { + a <- 1000 + eval_tidy(enexpr(expr), df) +} + +a <- 10 +with2(df, x + a) + +with3 <- function(df, expr) { + a <- 1000 + eval_tidy(enquo(expr), df) +} + +a <- 20 +with3(df, x + a) +rm(list = "a") +``` + +## Quosures + +**Quosure** bundles an expression with an environment. + +Means you should use `enquo()` instead of `enexpr()` when you use a data mask. + +# Ch 18 Expressions + +## Introduction + +`expr()` returns an expression, which is an object that captures the structure of the code without evaluating it. + +## Abstract syntax trees + +```{r} +lobstr::ast(f(x, "y", 1)) +#> █─f +#> ├─x +#> ├─"y" +#> └─1 +``` + +ASTs describe evaluation order, nesting of function calls + +### Non-code components + Don't capture whitespace/comments except to distinguish `<-` and `<` `-` + + + + + + + + From 91340bc3e1a10f2e2bd7c62f752afebe9fbb7476 Mon Sep 17 00:00:00 2001 From: Margaret Hannum Date: Wed, 18 Dec 2019 15:46:23 -0500 Subject: [PATCH 10/12] Update ch17_metaprogramming_big_picture.Rmd --- .../ch17_metaprogramming_big_picture.Rmd | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/advanced_r/ch17_metaprogramming_big_picture.Rmd b/advanced_r/ch17_metaprogramming_big_picture.Rmd index af24160..a503475 100644 --- a/advanced_r/ch17_metaprogramming_big_picture.Rmd +++ b/advanced_r/ch17_metaprogramming_big_picture.Rmd @@ -169,7 +169,74 @@ lobstr::ast(f(x, "y", 1)) ASTs describe evaluation order, nesting of function calls ### Non-code components - Don't capture whitespace/comments except to distinguish `<-` and `<` `-` +ASTs don't capture whitespace/comments except to distinguish `<-` and `<` `-` + +### Infix calls + +"Infix" notation is where operators are "infixed" between operands (e.g. y <- a + b). + +### Exercises + +1. Reconstruct code represented by trees +```{r} +lobstr::ast(f(g(h()))) + +lobstr::ast(1+2+3) + +lobstr::ast((x+y)*z) +``` + +2. Draw trees by hand and check answers with `lobstr::ast()` + +```{r} +lobstr::ast(f(g(h(i(1, 2, 3))))) +lobstr::ast(f(1, g(2, h(3, i())))) +lobstr::ast(f(g(1, 2), h(3, i(4, 5)))) +``` + +3. What's happening with the ASTs below? + + 1. variable x added to variable y + +```{r} +lobstr::ast(`x` + `y`) +#> █─`+` +#> ├─x +#> └─y +lobstr::ast(x + y) +``` + + 2. Variable x raised to power of y. Per documentation, "** is translated in the parser to ^, but this was undocumented for many years." + +```{r} + +lobstr::ast(x ** y) +#> █─`^` +#> ├─x +#> └─y +``` + + 3. Parser prefers '<-' over '->' +```{r} +lobstr::ast(1 -> x) +#> █─`<-` +#> ├─x +#> └─1 +``` + +4. What is special about the AST below? + +`srcref` is short for source reference. It's not specified in the expression within the ast +```{r} +lobstr::ast(function(x = 1, y = 2) {}) +#> █─`function` +#> ├─█─x = 1 +#> │ └─y = 2 +#> ├─█─`{` +#> └─ +``` + + From 8d1ba9a69dbfd31c5e7816b3a9434711dd443dbb Mon Sep 17 00:00:00 2001 From: Margaret Hannum Date: Fri, 20 Dec 2019 12:00:24 -0500 Subject: [PATCH 11/12] add more ch 18 --- .../ch17_metaprogramming_big_picture.Rmd | 317 ++++++++++++++++++ 1 file changed, 317 insertions(+) diff --git a/advanced_r/ch17_metaprogramming_big_picture.Rmd b/advanced_r/ch17_metaprogramming_big_picture.Rmd index a503475..5a4d7a1 100644 --- a/advanced_r/ch17_metaprogramming_big_picture.Rmd +++ b/advanced_r/ch17_metaprogramming_big_picture.Rmd @@ -237,6 +237,323 @@ lobstr::ast(function(x = 1, y = 2) {}) ``` +## Expressions + +*Expression* = "any member of the set of base types created by parsing code: constant scalars, symbols, call objects, and pairlists. + +### Constants + +Constant is either NULL or length-1 atomic vector +```{r} +identical(expr(TRUE), TRUE) +#> [1] TRUE +identical(expr(1), 1) +#> [1] TRUE +identical(expr(2L), 2L) +#> [1] TRUE +identical(expr("x"), "x") +#> [1] TRUE +``` + +### Symbols + +Symbol represents the name of an object + +***`rlang::sym()` seems it could be useful? + +```{r} +sym("x") + +syms(c("x", "y")) +``` + +Can go back with `as.character()` or `rlang::as_string()`. + +### Calls + +Call object is a special type of list - where first element is function call. + +```{r} +lobstr::ast(read.table("important.csv", row.names = FALSE)) +#> █─read.table +#> ├─"important.csv" +#> └─row.names = FALSE +x <- expr(read.table("important.csv", row.names = FALSE)) +``` + + +#### Subsetting +Can use standard subsetting on calls because they behave like lists! + + +* can we talk about the differences between the below? + +```{r} +x[[1]] + +x[[2]] + +x[[3]] + +x[-1] + +x[1] + +x[2] +``` + +*** I love that calls can be modified like lists + + + +#### Function position + +Possible that first element of call object (i.e. "function position") isn't a symbol, might have to be retrieved. + +#### Constructing + +Simple expressions can be constructed using `rlang::call2()`. + +```{r} +# first argument name of +call2("mean", x = expr(x), na.rm = TRUE) +#> mean(x = x, na.rm = TRUE) +call2(expr(base::mean), x = expr(x), na.rm = TRUE) +#> base::mean(x = x, na.rm = TRUE) +``` + + +### Exercises + +1. Which two of the six types of atomic vector can't appear in an expression? + +A: Have to be atomic of length 1. e.g. can't be complex atomics or raws. +Only 4 allowed are: int, num, chr, lgl. + +2. What happens when you subset a call object to remove the first element? + +```{r} +y <- expr(read.csv("foo.csv", header = TRUE))[-1] + +is.expression(y) +``` + +Object is no longer an expression with out the function call. The first element is now the 2nd element of the original. + +3. Describe the differences: + +```{r} +x <- 1:10 + +call2(median, x, na.rm = TRUE) +eval(call2(median, x, na.rm = TRUE)) + +call2(expr(median), x, na.rm = TRUE) +eval(call2(expr(median), x, na.rm = TRUE)) + +call2(median, expr(x), na.rm = TRUE) +eval(call2(median, expr(x), na.rm = TRUE)) + + +call2(expr(median), expr(x), na.rm = TRUE) +eval(call2(expr(median), expr(x), na.rm = TRUE)) +``` + +All are evaluated to the same response, but differ on when `x` and `median()` are evaluated. + +4. `rlang::call_standardise()` doesn't work so well for `mean()` since `mean()` uses the `...` argument which is difficult to standardize. + + +```{r} +library(rlang) + +call_standardise(quote(mean(1:10, na.rm = TRUE))) +#> mean(x = 1:10, na.rm = TRUE) +call_standardise(quote(mean(n = T, 1:10))) +#> mean(x = 1:10, n = T) +call_standardise(quote(mean(x = 1:10, , TRUE))) +#> mean(x = 1:10, , TRUE) + +``` + +Try to use `mean.default()` instead. + +5. Why doesn't this code make sense? + +```{r} +x <- expr(foo(x = 1)) +names(x) <- c("x", "") + + +x + +names(x) <- c("", "x") + +x +``` + +Doesn't make sense to name a function call? + +6. Construct the expression `if(x > 1) "a" else "b"` using multiple `call2()`. + +```{r} +x = 10 +# first attempt +call2("ifelse", expr(x > 1), "a", "b") + + +# second attempt +#call2("if", call2(">", x, 1), "a", "b") + +# finally +call2("if", call2(">", sym("x"), 1), "a", "b") + +# check with eval +eval(call2("if", call2(">", sym("x"), 1), "a", "b")) + +x = 0 +eval(call2("if", call2(">", sym("x"), 1), "a", "b")) +``` + +## Parsing and grammar + +"Parsing" = "process by which a computer language takes a string and constructs an expression" by rules of that languages "grammar". + +### Operator precedence + +Just like in school maths :) + +EXCEPT the `!` has low precedence! + +```{r} +# use this to see operator description and precedence +?Syntax +``` + +* Note parenthesis as a call in AST + +```{r} +lobstr::ast((1 + 2) * 3) +#> █─`*` +#> ├─█─`(` +#> │ └─█─`+` +#> │ ├─1 +#> │ └─2 +#> └─3 +``` + +### Associativity + +Most operators are left associative except for exponentiation and assignment. + +### Parsing and deparsing +What to do when you have code stored in a string... and want to parse it yourself! + +```{r} +x1 <- "y <- x + 10" +x1 +#> [1] "y <- x + 10" +is.call(x1) +#> [1] FALSE + +x2 <- rlang::parse_expr(x1) +x2 +#> y <- x + 10 +is.call(x2) +#> [1] TRUE +``` + +* Use `rlang::parse_exprs()` for a list of expressions. + +### Exercises +1. R uses parentheses in two slightly different ways: compare and contrast + +```{r} +f((1)) +`(`(1 + 1) +``` + +Parenthesis can be part of a function or just part of syntax.. + +When used in `f()` the parenthesis is part of syntax, while the inner one around 1 (`(1)`) is a function. + +I don't know exactly why the second version only calls one `(` function, see my second example. +```{r} +ast(f((1))) + +ast(`(`(1 + 1)) +#equivalent to this for some reason?? +ast((1 + 1)) + +# and here we have 2 `(` functions in the AST tree +ast(((1 + 1))) +``` + +2. `=` can be used 2 ways. show an example. + +```{r} +# assignment and as part of syntax of function calls + +df = data.frame(x = 2) + +``` + +3. Does `-2^2` yield 4 or -4? + +Yields -4 because of syntax precedence. `^` takes precedence over `-` +```{r} +-2^2 + +?Syntax +``` + +4. Why does `!1 + !1` return what it does? + +```{r} +!1 + !1 + +ast(!1 + !1) + +!1 + +1 + !1 +``` + +Unexpected (maybe) evaluation + +5. Why does `x1 <- x2 <- x3 <- 0 ` work? Describe two reasons + +```{r} +x1 <- x2 <- x3 <- 0 +``` + +Assignment goes right to left (as does `^`). + +Had to look up other reason: that `<-` "invisibly returns the value on the right hand side". + +```{r} +(x3 <- 0) +``` + +6. Compare ASTs of x + y %+% z and x ^ y %+% z. + +```{r} +ast(x + y %+% z) + +?`%+%` + +ast(x ^ y %+% z) +``` + +Custom infix `%+%` seems to have precedence over `+` but not over `^`. + + + + + + + + From e1884f729b0d844ca916d5182df64c27d47cc833 Mon Sep 17 00:00:00 2001 From: Margaret Hannum Date: Fri, 20 Dec 2019 13:14:24 -0500 Subject: [PATCH 12/12] ch 18 updates --- .../ch17_metaprogramming_big_picture.Rmd | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/advanced_r/ch17_metaprogramming_big_picture.Rmd b/advanced_r/ch17_metaprogramming_big_picture.Rmd index 5a4d7a1..35415ee 100644 --- a/advanced_r/ch17_metaprogramming_big_picture.Rmd +++ b/advanced_r/ch17_metaprogramming_big_picture.Rmd @@ -152,6 +152,7 @@ Means you should use `enquo()` instead of `enexpr()` when you use a data mask. # Ch 18 Expressions + ## Introduction `expr()` returns an expression, which is an object that captures the structure of the code without evaluating it. @@ -234,6 +235,8 @@ lobstr::ast(function(x = 1, y = 2) {}) #> │ └─y = 2 #> ├─█─`{` #> └─ + + ``` @@ -295,11 +298,19 @@ x[[2]] x[[3]] -x[-1] +is.expression(x[-1]) +is.expression(x) +class(x) x[1] x[2] + +as.list(x) + +lobstr::ast(!!x) + +x$sep <- "" ``` *** I love that calls can be modified like lists @@ -335,7 +346,10 @@ Only 4 allowed are: int, num, chr, lgl. ```{r} y <- expr(read.csv("foo.csv", header = TRUE))[-1] -is.expression(y) +is_call(y) +is_expression(y) + +y$sep <- "" ``` Object is no longer an expression with out the function call. The first element is now the 2nd element of the original. @@ -374,6 +388,9 @@ call_standardise(quote(mean(n = T, 1:10))) call_standardise(quote(mean(x = 1:10, , TRUE))) #> mean(x = 1:10, , TRUE) +call_standardise(quote(mean.default(x = 1:10, , TRUE))) + + ``` Try to use `mean.default()` instead. @@ -409,6 +426,7 @@ call2("ifelse", expr(x > 1), "a", "b") call2("if", call2(">", sym("x"), 1), "a", "b") # check with eval +x = 10 eval(call2("if", call2(">", sym("x"), 1), "a", "b")) x = 0 @@ -481,6 +499,7 @@ I don't know exactly why the second version only calls one `(` function, see my ```{r} ast(f((1))) + ast(`(`(1 + 1)) #equivalent to this for some reason?? ast((1 + 1)) @@ -540,6 +559,10 @@ Had to look up other reason: that `<-` "invisibly returns the value on the right ```{r} ast(x + y %+% z) +!%in% + + !( %in% ) + ?`%+%` ast(x ^ y %+% z)