GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — Advanced-R-Solutions/05-Environments.Rmd at master · glidis/Advanced-R-Solutions Expand file tree/ Copy path05-Environments.RmdMore file actions 190 lines (155 loc) · 7.41 KB / Copy path05-Environments.RmdFile metadata and controls190 lines (155 loc) · 7.41 KB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 # Environments ## Environment basics 1. __<span style="color:red">Q</span>__: List three ways in which an environment differs from a list. __<span style="color:green">A</span>__: The most important differences are: * environments have reference semantics * environments have parents * environments are not ordered * elements of environments need to be (uniquely) named 2. __<span style="color:red">Q</span>__: If you don't supply an explicit environment, where do `ls()` and `rm()` look? Where does `<-` make bindings? The __<span style="color:green">A</span>__: `ls()` and `rm` look in their calling environments which they find by `as.environment(-1)`. From the book: > Assignment is the act of binding (or rebinding) a name to a value in an environment. From `` ?`<-` ``: > The operators `<-` and `=` assign into the environment in which they are evaluated. The operator `<-` can be used anywhere, whereas the operator `=` is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions. 3. __<span style="color:red">Q</span>__: Using `parent.env()` and a loop (or a recursive function), verify that the ancestors of `globalenv()` include `baseenv()` and `emptyenv()`. Use the same basic idea to implement your own version of `search()`. __<span style="color:green">A</span>__: We can print the ancestors for example by using a recursive function: ```{r, eval = FALSE} ancestors <- function(env = globalenv()){ if (identical(env, emptyenv())) { print(env)} else { print(env) ancestors(parent.env(env)) } } ancestors() ``` To implement a new version of `search()` we use a while statement: ```{r, eval = FALSE} search2 <- function(env = globalenv()){ envs <- character() while (!identical(env, emptyenv())) { ename <- environmentName(env) if (ename == "base") ename <- "package:base" if (ename == "R_GlobalEnv") ename <- ".GlobalEnv" envs <- c(envs, ename) env <- parent.env(env) } return(envs) } ``` ## Recursing over environments 1. __<span style="color:red">Q</span>__: Modify `where()` to find all environments that contain a binding for `name`. __<span style="color:green">A</span>__: We look at the source code of the original `pryr::where()`: ```{r, eval = FALSE} pryr::where function (name, env = parent.frame()) { stopifnot(is.character(name), length(name) == 1) env <- to_env(env) if (identical(env, emptyenv())) { stop("Can't find ", name, call. = FALSE) } if (exists(name, env, inherits = FALSE)) { env } else { where(name, parent.env(env)) # we will copy this line in the last if block } } ``` Since `where()` stops searching when a match appears, we copy the recursive call in the `else` block to the `block` of the matching case, so that our new function `where2` will look for a binding within the complete search path. ```{r, eval = FALSE} where2 <- function (name, env = parent.frame()) { stopifnot(is.character(name), length(name) == 1) env <- to_env(env) if (identical(env, emptyenv())) { stop("Can't find ", name, call. = FALSE) } if (exists(name, env, inherits = FALSE)) { env where(name, parent.env(env)) } else { where(name, parent.env(env)) } } ``` 2. __<span style="color:red">Q</span>__: Write your own version of `get()` using a function written in the style of `where()`. __<span style="color:green">A</span>__: 3. __<span style="color:red">Q</span>__: Write a function called `fget()` that finds only function objects. It should have two arguments, `name` and `env`, and should obey the regular scoping rules for functions: if there's an object with a matching name that's not a function, look in the parent. For an added challenge, also add an `inherits` argument which controls whether the function recurses up the parents or only looks in one environment. __<span style="color:green">A</span>__: 4. __<span style="color:red">Q</span>__: Write your own version of `exists(inherits = FALSE)` (Hint: use `ls()`.) Write a recursive version that behaves like `exists(inherits = TRUE)`. __<span style="color:green">A</span>__: ## Function environments 1. __<span style="color:red">Q</span>__: List the four environments associated with a function. What does each one do? Why is the distinction between enclosing and binding environments particularly important? __<span style="color:green">A</span>__: * Enclosing: where the function is created * Binding: where the function was assigned * Execution: a temporary environment which is created when the function is executed * Calling: the environment from where the function was called The difference between binding and enclosing environment is important, because of R's lexical scoping rules. If R can't find an object in the current environment while executing a function, it will look for it in the enclosing environment. 2. __<span style="color:red">Q</span>__: Draw a diagram that shows the enclosing environments of this function: ```{r, eval = FALSE} f1 <- function(x1) { f2 <- function(x2) { f3 <- function(x3) { x1 + x2 + x3 } f3(3) } f2(2) } f1(1) ``` __<span style="color:green">A</span>__: 3. __<span style="color:red">Q</span>__: Expand your previous diagram to show function bindings. __<span style="color:green">A</span>__: 4. __<span style="color:red">Q</span>__: Expand it again to show the execution and calling environments. __<span style="color:green">A</span>__: 5. __<span style="color:red">Q</span>__: Write an enhanced version of `str()` that provides more information about functions. Show where the function was found and what environment it was defined in. __<span style="color:green">A</span>__: ## Binding names to values 1. __<span style="color:red">Q</span>__: What does this function do? How does it differ from `<<-` and why might you prefer it? ```{r, error = TRUE} rebind <- function(name, value, env = parent.frame()) { if (identical(env, emptyenv())) { stop("Can't find ", name, call. = FALSE) } else if (exists(name, envir = env, inherits = FALSE)) { assign(name, value, envir = env) } else { rebind(name, value, parent.env(env)) } } rebind("a", 10) a <- 5 rebind("a", 10) a ``` __<span style="color:green">A</span>__: 2. __<span style="color:red">Q</span>__: Create a version of `assign()` that will only bind new names, never re-bind old names. Some programming languages only do this, and are known as [single assignment languages][single assignment]. __<span style="color:green">A</span>__: 3. __<span style="color:red">Q</span>__: Write an assignment function that can do active, delayed, and locked bindings. What might you call it? What arguments should it take? Can you guess which sort of assignment it should do based on the input? __<span style="color:green">A</span>__: |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |