26  Quasiquotation

准引用 Quasiquotation

拟引函数 quasiquoting functions

引用(quotation)是捕获未评估表达式的行为

取消引用(unquotation)!! (发音为bang-bang)是选择性地评估引用的表达式的部分,告诉引用函数删除隐式引号

准引用(Quasiquotation)使得创建将函数作者编写的代码与函数用户编写的代码相结合的函数变得容易

Show the code
Show the code
paste("Good", "morning", "Hadley")
#> [1] "Good morning Hadley"


cement <- function(...) {
  args <- ensyms(...)
  paste(purrr::map(args, as_string), collapse = " ")
}

cement(Good, morning, Hadley)
#> [1] "Good morning Hadley"

取消引用(unquotation)!! (发音为bang-bang)告诉引用函数删除隐式引号

Show the code
name <- "Hadley"
time <- "morning"

# quoted
paste("Good", time, name)
#> [1] "Good morning Hadley"

# evaluated
cement(Good, time, name)
#> [1] "Good time name"
cement(Good, !!time, !!name)
#> [1] "Good morning Hadley"

26.1 引用

26.1.1 捕获表达式

空格和注释不是表达式的一部分

捕获(开发人员)形参的表达式,函数主体中

Show the code
expr(x + y)
#> x + y
expr(1 / 2 / 3)
#> 1/2/3

exprs(x = x ^ 2, y = y ^ 3, z = z ^ 4)
#> $x
#> x^2
#> 
#> $y
#> y^3
#> 
#> $z
#> z^4


f1 <- function(x) expr(x)
f1(a + b + c)
#> x

捕获用户作为参数传入的表达式,通过传参提供

Show the code
f2 <- function(x) enexpr(x)
f2(a + b + c)
#> a + b + c

# 捕获多个表达式
f <- function(...) enexprs(...)
f(x = 1, y = 10 * z)
#> $x
#> [1] 1
#> 
#> $y
#> 10 * z

f(mtcars$mpg)
#> [[1]]
#> mtcars$mpg

26.1.2 捕获 symbol

ensym()

ensyms()

Show the code
f <- function(...) ensyms(...)
f(x)
#> [[1]]
#> x
f("x")
#> [[1]]
#> x

26.1.3 替换

Show the code
f4 <- function(x) substitute(x * 2)
f4(a + b + c)
#> (a + b + c) * 2

26.2 取消引用

26.2.1 取消引用一个参数

Show the code
x <- expr(-1)
expr(f(!!x, y))
#> f(-1, y)

a <- sym("y")
b <- 1
expr(f(!!a, !!b))
#> f(y, 1)

右边是函数调用,评估并插入结果

Show the code
mean_rm <- function(var) {
  var <- ensym(var)
  expr(mean(!!var, na.rm = TRUE))
}


expr(mean_rm(x) + mean_rm(y))
#> mean_rm(x) + mean_rm(y)

expr(!!mean_rm(x) + !!mean_rm(y))
#> mean(x, na.rm = TRUE) + mean(y, na.rm = TRUE)

保留运算符的优先级

Show the code
x1 <- expr(x + 1)
x2 <- expr(x + 2)

expr(!!x1 / !!x2)
#> (x + 1)/(x + 2)

26.2.2 取消引用函数

Show the code
f <- expr(pkg::foo)
f
#> pkg::foo
expr((!!f)(x, y))
#> pkg::foo(x, y)

call2(f, expr(x), expr(y))
#> pkg::foo(x, y)

26.2.3 取消引用缺失的参数

Show the code

arg <- missing_arg()

expr(foo(!!arg, !!arg))
#> Error in eval(expr, envir, enclos): argument "arg" is missing, with no default

expr(foo(!!maybe_missing(arg), !!maybe_missing(arg)))
#> foo(, )

26.2.4 取消引用特殊形式函数参数

Show the code
x <- expr(mpg)
expr(`$`(mtcars, !!x))
#> mtcars$mpg

26.2.5 取消引用多个参数

unquote-splice !!! ,发音为 bang-bang-bang,一对多的替换插入

Show the code
xs <- exprs(1, a, -b)
expr(f(!!!xs, y))
#> f(1, a, -b, y)


# Or with names
ys <- set_names(xs, c("a", "b", "c"))
expr(f(!!!ys, d = 4))
#> f(a = 1, b = a, c = -b, d = 4)


call2("f", !!!xs, expr(y))
#> f(1, a, -b, y)

26.2.6 点-点-点 ...

与 Python 中的 argskwarg(star-star-kwargs) 密切相关

Show the code
dfs <- list(
  a = data.frame(x = 1, y = 2),
  b = data.frame(x = 3, y = 4)
)

dplyr::bind_rows(!!!dfs)
x y
1 2
3 4
Show the code

do.call("rbind", dfs)
x y
a 1 2
b 3 4

间接提供参数名称,:= 发音为 colon-equals

Show the code
var <- "x"
val <- c(4, 3, 9)

tibble::tibble(!!var := val)
x
4
3
9
Show the code

args <- list(val)
names(args) <- var

do.call("data.frame", args)
x
4
3
9