18.17 Example 2: Finding all variables created by assignment
- Listing all the variables is a little more complicated.
- Figure out what assignment looks like based on the AST.
- Now we need to decide what data structure we’re going to use for the results.
- Easiest thing will be to return a character vector.
- We would need to use a list if we wanted to return symbols.
18.17.2 Dealing with the recursive cases
- Here is the function to flatten pairlists.
flat_map_chr <- function(.x, .f, ...) {
purrr::flatten_chr(purrr::map(.x, .f, ...))
}
flat_map_chr(letters[1:3], ~ rep(., sample(3, 1)))
#> [1] "a" "a" "b" "b" "c"
#> [1] "a" "b" "b" "b" "c" "c" "c"
- Here is the code needed to identify calls.
find_assign_rec <- function(x) {
switch_expr(x,
# Base cases
constant = ,
symbol = character(),
# Recursive cases
pairlist = flat_map_chr(as.list(x), find_assign_rec),
call = {
if (is_call(x, "<-")) {
as_string(x[[2]])
} else {
flat_map_chr(as.list(x), find_assign_rec)
}
}
)
}
find_assign(a <- 1)
#> [1] "a"
find_assign({
a <- 1
{
b <- 2
}
})
#> [1] "a" "b"
18.17.3 Make the function more robust
- Throw cases at it that we think might break the function.
- Write a function to handle these cases.
find_assign_call <- function(x) {
if (is_call(x, "<-") && is_symbol(x[[2]])) {
lhs <- as_string(x[[2]])
children <- as.list(x)[-1]
} else {
lhs <- character()
children <- as.list(x)
}
c(lhs, flat_map_chr(children, find_assign_rec))
}
find_assign_rec <- function(x) {
switch_expr(x,
# Base cases
constant = ,
symbol = character(),
# Recursive cases
pairlist = flat_map_chr(x, find_assign_rec),
call = find_assign_call(x)
)
}
find_assign(a <- b <- c <- 1)
#> [1] "a" "b" "c"
find_assign(system.time(x <- print(y <- 5)))
#> [1] "x" "y"
- This approach certainly is more complicated, but it’s important to start simple and move up.