18.16 Specific use cases for recurse_call()

18.16.1 Example 1: Finding F and T

  • Using F and T in our code rather than FALSE and TRUE is bad practice.
  • Say we want to walk the AST to find times when we use F and T.
  • Start off by finding the type of T vs TRUE.
expr_type(expr(TRUE))
# [1] "constant"

expr_type(expr(T))
# [1] "symbol"
  • With this knowledge, we can now write the base cases of our recursive function.
  • The logic is as follows:
    • A constant is never a logical abberviation and a symbol is an abbreviation if it’s “F” or “T”:
logical_abbr_rec <- function(x) {
  switch_expr(x,
    constant = FALSE,
    symbol = as_string(x) %in% c("F", "T")
  )
}
logical_abbr_rec(expr(TRUE))
# [1] FALSE
logical_abbr_rec(expr(T))
# [1] TRUE
  • It’s best practice to write another wrapper, assuming every input you receive will be an expression.
logical_abbr <- function(x) {
  logical_abbr_rec(enexpr(x))
}

logical_abbr(T)
#> [1] TRUE
# [1] TRUE
logical_abbr(FALSE)
#> [1] FALSE
# [1] FALSE

18.16.2 Next step: code for the recursive cases

  • Here we want to do the same thing for calls and for pairlists.
  • Here’s the logic: recursively apply the function to each subcomponent, and return TRUE if any subcomponent contains a logical abbreviation.
  • This is simplified by using the purrr::some() function, which iterates over a list and returns TRUE if the predicate function is true for any element.
logical_abbr_rec <- function(x) {
  switch_expr(x,
  # Base cases
  constant = FALSE,
  symbol = as_string(x) %in% c("F", "T"),
  # Recursive cases
  call = ,
  # Are we sure this is the correct function to use?
  # Why not logical_abbr_rec?
  pairlist = purrr::some(x, logical_abbr_rec)
  )
}

logical_abbr(mean(x, na.rm = T))
#> [1] TRUE
# [1] TRUE

logical_abbr(function(x, na.rm = T) FALSE)
#> [1] TRUE
# [1] TRUE