-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path06-Functions.Rmd
259 lines (183 loc) · 7.82 KB
/
06-Functions.Rmd
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
---
title: "5 - Functions"
author: "Jonny Saunders"
date: "October 5, 2017"
output:
md_document:
preserve_yaml: true
toc: true
toc_depth: 2
order: 2
---
# Functions
## Form
The general form of a function is
```{r, eval=FALSE}
function_name <- function(arguments){
#Do something
return(output)
}
```
Functions have:
1. An argument list, or `formals()` - what is passed to the function
2. A `body()`. Typically, this is the code between `{curly braces}`, but can be any syntactically complete statement - `{}` denote a code "block" that is not evaluated until the end of the block, rather than evaluated line-by-line as is otherwise the case.
3. An `environment()`, or the list of symbol (name)/value pairs known to the function.
For example...
```{r Example of a Function}
inverse <- function(invert_me){
inverse <- 1/invert_me
return(inverse)
}
inverse(2)
```
We can interrogate the parts of a function
```{r}
formals(inverse)
body(inverse)
environment(inverse)
```
## Environments and Scoping
### Function Environments
Functions create their own private collection of variables that are distinct from those in the global environment.
For example, what's going on here?
```{r}
x <- 1
y <- 3
whos_who <- function(){
x <- 2
print(paste("x is", x))
print(paste("y is", y))
}
whos_who()
print(paste("but x is also", x))
```
We define x and y to be 1 and 3 respectively, and then a function that defines x to be 2. When we call the function, it tells us that x is 2 and 3, but outside the function x is 1.
The assignment `x <- 2` is **scoped**, or defined locally in the function's environment. **Lexical scoping** is the process by which functions lookup the values associated with different names. In this case, `x` is defined locally, but `y` is not. Functions inherit the enviroment that they were defined in, or their **enclosing environment**, in this case the global environment -- a function and its attached environment is called a **closure**.
```{r}
environment(whos_who)
```
If a function doesn't find a variable locally, it ascends the environments it is nested within until it finds (or doesn't find) a match. If one defines a function within another function, the innermost function will preserve the environment of the outer function -- which can be used to make "function factories:"
```{r}
# example from http://adv-r.had.co.nz/Environments.html#function-envs
plus <- function(x){
function(y){
x + y
}
}
plus_one <- plus(1)
plus_two <- plus(2)
plus_one(1)
plus_two(2)
```
the `plus` function itself returns a function with the environment created when `plus` is called. Since `x` is defined in the returned function's (eg. `plus_one`) **enclosing environment** it will always be able to use that value even if there is another `x` in the global environment.
While a function will always preserve its enclosing environment, its **execution environment**, or the environment created when the function is called, is created fresh each time. This **fresh start** principle gives functions their utility as dissociable building blocks for more complicated code -- if a function does one thing and does it well, it can be used in whatever context that operation needs to be performed without fear or prejudice from its previous experiences.
Functions also have a **binding environment(s)**, or the environments that have a binding between a name and the function. We will skip these for now as they will become more relevant when it comes time to write packages, but more information can be found here: [http://adv-r.had.co.nz/Environments.html#function-envs](Advanced R - Function Environments)
### Querying environments
The global environment, which we are used to working in, is itself nested within a series of environments:
1. A series of package environments (or **namespaces**) that provide the names of any loaded (`library/require()`'d) packages
2. The base environment that provides the names of the base functions
3. The empty environment, the parent of all environments.
![http://adv-r.had.co.nz/Environments.html](guide_files/globalenv.png)
You can see the loaded packages, or search path, with `search`
```{r}
search()
```
One can make a symbol to reference an environment with `as.environment`
```{r}
env <- as.environment('.GlobalEnv')
```
and query its contents with `ls.str()`
```{r}
ls.str(env)
```
if you're lost deep in a twisted spire of environments and need to get something specifically, you can do so with `get` (although fixing the structure of your code is usually a better idea ;p)
```{r}
# create a new environment
new_env <- new.env()
# assignation works like a list
new_env$new_var <- 5
# attaching an environment adds it to our search path
attach(new_env)
search()
new_var
detach(new_env)
# we now make a new assignation in the global environment, which will be searched before any attached environment
new_var <- 10
new_var
attach(new_env)
new_var
detach(new_env)
# we can skip the masking of the global environment with get
get("new_var", envir=new_env)
```
## Arguments
The names of the variables used within the function and the structure of their input are the **formal arguments** of a function, but the variables that are given to a function each time are also called arguments. Where ambiguous, refer to the latter as arguments.
Arguments can be specified by position or name. Partial names also work, but that typically makes for confusing code. Arguments are first matched by name and then by position.
```{r}
profit_maker <- function(good_idea, stealing, depth){
print(paste("Momma said i should", good_idea,
"but instead I stole", stealing,
"dollars and buried it in a hole", depth, "feet deep"))
}
profit_maker("say mean things about barn animals", 100, 1000)
profit_maker(depth=100, stealing=1, good_idea="keep my bellybutton open")
```
Formal arguments can be defined with default values, so if an argument is not passed the default is used.
```{r}
whats_it_cost <- function(cost=10){
print(paste("i dunno like", cost, "bucks"))
}
whats_it_cost()
```
This is useful in situations where a function should do one thing most of the time, but if asked nicely should do something else.
```{r}
ice_cream <- function(scoops, round=FALSE){
if (round == FALSE){
print(paste("here ya go kid,", scoops, "scoops of hot fresh ice cream"))
} else {
print(paste("listen kid i'm gonna give you", floor(scoops), "scoops and you can get out of my shop"))
}
}
what_i_want <- runif(1)*10
ice_cream(what_i_want)
ice_cream(what_i_want, round=TRUE)
```
Default arguments can be used to make code more efficient while still being flexible. For example if the same value needs to be computed in a number of different functions, it can instead be passed. If the default is set to NULL you can check if you need to perform the computation.
```{r}
# precompute some image's x gradient stupidly
image <- matrix(runif(100), nrow=10, ncol=10)
grad <- diff(image)
image_operation_1 <- function(image, grad=NULL){
if (is.null(grad)){
grad <- diff(image)
}
# do some stuff to the image
}
image_operation_2 <- function(image, grad=NULL){
if (is.null(grad)){
grad <- diff(image)
}
# do some stuff to the image
}
```
Additionally, oen can use `...` to specify an arbitrary number of arguments. This is commonly used in the S3 object system where the arguments passed as `...` are simply passed on to the method calls from the generic method.
```{r}
print_whatever <- function(...){
args <- list(...)
anames <- names(args)
for (a in anames){
print(paste(a, ":", args[a]))
}
}
print_whatever(apple="banana", coconut="hospital visit")
```
## Todo:
* infix functions
* Scoping from R Faq 3.3.1
```{r, eval=FALSE}
# S4 Methods are stored in environments
nM <- asNamespace("Matrix")
sort(grep("^[.]__T__", names(nM), value=TRUE))
meth.Ops <- nM$`.__T__Ops:base`
head(sort(names(meth.Ops)))
```