metaprogramming
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseR Metaprogramming with rlang
基于rlang的R元编程
Metaprogramming is the ability to defuse, create, and inject R expressions. The core pattern is defuse-and-inject: capture code as data, optionally transform it, then inject it into another context for evaluation.
元编程指的是对R表达式进行延迟求值、创建和注入的能力。核心模式是延迟求值-注入:将代码捕获为数据,可选择对其进行转换,然后将其注入到另一个上下文中执行。
Quick Reference
速查参考
| Task | Function/Operator |
|---|---|
| Defuse your own expression | |
| Defuse user's single argument | |
Defuse user's | |
| Inject single expression | |
| Splice list of expressions | |
| Get expression from quosure | |
| Get environment from quosure | |
| Build symbol from string | |
| Build symbol with .data pronoun | |
| Build symbols from vector | |
| Auto-label expression | |
| Format argument as string | |
| Interpolate name in dynamic dots | |
| Interpolate argument in name | |
| 任务 | 函数/操作符 |
|---|---|
| 延迟求值自定义表达式 | |
| 延迟求值用户的单个参数 | |
延迟求值用户的 | |
| 注入单个表达式 | |
| 拼接注入表达式列表 | |
| 从quosure中提取表达式 | |
| 从quosure中提取环境 | |
| 从字符串构建符号 | |
| 使用.data代词构建符号 | |
| 从向量构建符号 | |
| 自动为表达式添加标签 | |
| 将参数格式化为字符串 | |
| 在动态点中插值名称 | |
| 在名称中插值参数 | |
Defusing Expressions
延迟求值表达式
Defusing stops evaluation and returns the expression as a tree-like object (a "blueprint" for computation).
r
undefined延迟求值会停止表达式的执行,将其作为树形对象返回(即计算的“蓝图”)。
r
undefinedNormal evaluation returns result
常规执行返回结果
1 + 1
#> [1] 2
1 + 1
#> [1] 2
Defusing returns the expression
延迟求值返回表达式
expr(1 + 1)
#> 1 + 1
undefinedexpr(1 + 1)
#> 1 + 1
undefinedexpr() vs enquo()
expr() vs enquo()
| Function | Defuses | Returns | Use When |
|---|---|---|---|
| Your own code | Expression | Building expressions locally |
| User's argument | Quosure | Forwarding function arguments |
| User's | List of quosures | Forwarding multiple arguments |
r
undefined| 函数 | 延迟求值对象 | 返回值 | 使用场景 |
|---|---|---|---|
| 自定义代码 | 表达式 | 本地构建表达式 |
| 用户的参数 | Quosure | 转发函数参数 |
| 用户的 | Quosure列表 | 转发多个参数 |
r
undefinedDefuse your own expression
延迟求值自定义表达式
my_expr <- expr(mean(x, na.rm = TRUE))
my_expr <- expr(mean(x, na.rm = TRUE))
Defuse user's function argument (returns quosure)
延迟求值用户的函数参数(返回quosure)
my_function <- function(var) {
enquo(var)
}
my_function(cyl + am)
#> <quosure>
#> expr: ^cyl + am
#> env: global
undefinedmy_function <- function(var) {
enquo(var)
}
my_function(cyl + am)
#> <quosure>
#> expr: ^cyl + am
#> env: global
undefinedenquos() with .named
带.named参数的enquos()
Auto-label unnamed arguments:
r
g <- function(...) {
vars <- enquos(..., .named = TRUE)
names(vars)
}
g(cyl, 1 + 1)
#> [1] "cyl" "1 + 1"
g(foo = cyl, bar = 1 + 1)
#> [1] "foo" "bar"自动为未命名参数添加标签:
r
g <- function(...) {
vars <- enquos(..., .named = TRUE)
names(vars)
}
g(cyl, 1 + 1)
#> [1] "cyl" "1 + 1"
g(foo = cyl, bar = 1 + 1)
#> [1] "foo" "bar"Types of Defused Expressions
延迟求值表达式的类型
- Calls: ,
f(x, y)- function invocations1 + 1 - Symbols: ,
x- named object referencesdf - Constants: ,
1,"text"- literal valuesNULL
- 调用(Calls):、
f(x, y)- 函数调用1 + 1 - 符号(Symbols):、
x- 命名对象引用df - 常量(Constants):、
1、"text"- 字面量值NULL
Quosures
Quosure
A quosure wraps an expression with its original environment. This is critical for correct evaluation when expressions travel across function and package boundaries.
Quosure是将表达式与其原始环境封装在一起的对象。当表达式跨函数或包边界传递时,这对于确保正确执行至关重要。
Why Environments Matter
环境的重要性
r
undefinedr
undefinedIn package A
在包A中
my_function <- function(data, var) {
'var' was defined in the user's environment
The quosure tracks that environment
var <- enquo(var)
When passed to package B's function, the quosure
ensures symbols resolve in the correct environment
pkg_b_function(data, !!var)
}
Without environment tracking, symbols might resolve to wrong objects when code crosses package boundaries.my_function <- function(data, var) {
'var'是在用户环境中定义的
Quosure会追踪该环境
var <- enquo(var)
当传递给包B的函数时,Quosure
确保符号在正确的环境中解析
pkg_b_function(data, !!var)
}
如果没有环境追踪,当代码跨包边界时,符号可能会解析到错误的对象。When You Need Quosures
何时需要Quosure
| Situation | Use Quosure? |
|---|---|
| Defusing function arguments | Yes - use |
| Building local expressions | No - use |
| Cross-package composition | Yes - environments matter |
| Simple local evaluation | No - |
| 场景 | 是否需要Quosure? |
|---|---|
| 延迟求值函数参数 | 是 - 使用 |
| 构建本地表达式 | 否 - 使用 |
| 跨包组合代码 | 是 - 环境很重要 |
| 简单本地执行 | 否 - |
Quosure Operations
Quosure操作
r
q <- enquo(x + 1)
quo_get_expr(q) # Extract expression: x + 1
quo_get_env(q) # Extract environmentr
q <- enquo(x + 1)
quo_get_expr(q) # 提取表达式: x + 1
quo_get_env(q) # 提取环境Create quosure manually
手动创建Quosure
new_quosure(expr(x + 1), env = global_env())
new_quosure(expr(x + 1), env = global_env())
Convert expression to quosure
将表达式转换为Quosure
as_quosure(expr(x + 1), env = global_env())
undefinedas_quosure(expr(x + 1), env = global_env())
undefinedInjection Operators
注入操作符
Injection inserts defused expressions back into code before evaluation.
注入指的是在执行前将延迟求值的表达式重新插入到代码中。
{{
(Embrace)
{{{{
(Embrace)
{{Defuses and injects in one step. Equivalent to :
!!enquo(arg)r
undefined一步完成延迟求值和注入。等效于:
!!enquo(arg)r
undefinedThese are equivalent:
以下两种写法等效:
my_summarise <- function(data, var) {
data |> dplyr::summarise({{ var }})
}
my_summarise <- function(data, var) {
data |> dplyr::summarise(!!enquo(var))
}
Use `{{` when you simply need to forward an argument. Use `enquo()` + `!!` when you need to inspect or transform the expression first.my_summarise <- function(data, var) {
data |> dplyr::summarise({{ var }})
}
my_summarise <- function(data, var) {
data |> dplyr::summarise(!!enquo(var))
}
当仅需转发参数时使用`{{`。当需要先检查或转换表达式时,使用`enquo()` + `!!`。!!
(Bang-Bang)
!!!!
(Bang-Bang)
!!Injects a single expression:
r
var <- expr(cyl)
mtcars |> dplyr::summarise(mean(!!var))
#> Equivalent to: summarise(mean(cyl))注入单个表达式:
r
var <- expr(cyl)
mtcars |> dplyr::summarise(mean(!!var))
#> 等效于: summarise(mean(cyl))Inject a value to avoid name collisions
注入值以避免名称冲突
x <- 100
df |> dplyr::mutate(x = x / !!x)
#> Uses column x divided by env value 100
undefinedx <- 100
df |> dplyr::mutate(x = x / !!x)
#> 使用列x除以环境中的值100
undefined!!!
(Splice)
!!!!!!
(Splice)
!!!Injects each element of a list as separate arguments:
r
vars <- exprs(cyl, am, vs)
mtcars |> dplyr::select(!!!vars)
#> Equivalent to: select(cyl, am, vs)将列表中的每个元素作为单独参数注入:
r
vars <- exprs(cyl, am, vs)
mtcars |> dplyr::select(!!!vars)
#> 等效于: select(cyl, am, vs)With enquos()
结合enquos()使用
my_group_by <- function(.data, ...) {
.data |> dplyr::group_by(!!!enquos(...))
}
undefinedmy_group_by <- function(.data, ...) {
.data |> dplyr::group_by(!!!enquos(...))
}
undefinedWhere Operators Work
操作符的适用场景
- Data-masked arguments: Implicitly enabled (dplyr, ggplot2, etc.)
- inject(): Explicitly enables operators in any context
- Dynamic dots: and
!!!work in functions using{name}list2()
r
undefined- 数据掩码参数:默认支持(如dplyr、ggplot2等)
- inject():在任意上下文中显式启用操作符
- 动态点:和
!!!在使用{name}的函数中生效list2()
r
undefinedEnable injection in base functions
在基础R函数中启用注入
inject(
with(mtcars, mean(!!sym("cyl")))
)
undefinedinject(
with(mtcars, mean(!!sym("cyl")))
)
undefinedBuilding Expressions from Data
从数据构建表达式
sym() and syms()
sym() 和 syms()
Convert strings to symbols:
r
var <- "cyl"
sym(var)
#> cyl
vars <- c("cyl", "am")
syms(vars)
#> [[1]]
#> cyl
#> [[2]]
#> am将字符串转换为符号:
r
var <- "cyl"
sym(var)
#> cyl
vars <- c("cyl", "am")
syms(vars)
#> [[1]]
#> cyl
#> [[2]]
#> amdata_sym() and data_syms()
data_sym() 和 data_syms()
Create expressions (safer in tidy eval, avoids collisions):
.data$colr
data_sym("cyl")
#> .data$cyl
data_syms(c("cyl", "am"))
#> [[1]]
#> .data$cyl
#> [[2]]
#> .data$amUse for base R functions; use for tidy eval functions.
sym()data_sym()创建表达式(在tidy eval中更安全,避免冲突):
.data$colr
data_sym("cyl")
#> .data$cyl
data_syms(c("cyl", "am"))
#> [[1]]
#> .data$cyl
#> [[2]]
#> .data$am在基础R函数中使用;在tidy eval函数中使用。
sym()data_sym()Building Calls
构建调用表达式
r
undefinedr
undefinedWith call()
使用call()
call("mean", sym("x"), na.rm = TRUE)
#> mean(x, na.rm = TRUE)
call("mean", sym("x"), na.rm = TRUE)
#> mean(x, na.rm = TRUE)
With expr() and injection
使用expr()和注入
var <- sym("x")
expr(mean(!!var, na.rm = TRUE))
#> mean(x, na.rm = TRUE)
undefinedvar <- sym("x")
expr(mean(!!var, na.rm = TRUE))
#> mean(x, na.rm = TRUE)
undefinedName Interpolation (Glue Operators)
名称插值(Glue操作符)
In dynamic dots, use glue syntax for names.
在动态点中,使用glue语法处理名称。
{
for Variable Values
{{}
用于变量值
{}r
name <- "foo"
tibble::tibble("{name}" := 1:3)
#> # A tibble: 3 x 1
#> foo
#> <int>
#> 1 1
#> 2 2
#> 3 3
tibble::tibble("prefix_{name}" := 1:3)
#> Column named: prefix_foor
name <- "foo"
tibble::tibble("{name}" := 1:3)
#> # A tibble: 3 x 1
#> foo
#> <int>
#> 1 1
#> 2 2
#> 3 3
tibble::tibble("prefix_{name}" := 1:3)
#> 列名为: prefix_foo{{
for Argument Labels
{{{{}}
用于参数标签
{{}}r
my_mutate <- function(data, var) {
data |> dplyr::mutate("mean_{{ var }}" := mean({{ var }}))
}
mtcars |> my_mutate(cyl)
#> Creates column: mean_cylr
my_mutate <- function(data, var) {
data |> dplyr::mutate("mean_{{ var }}" := mean({{ var }}))
}
mtcars |> my_mutate(cyl)
#> 创建列: mean_cylenglue() for String Formatting
englue() 用于字符串格式化
r
my_function <- function(var) {
englue("Column: {{ var }}")
}
my_function(some_column)
#> [1] "Column: some_column"r
my_function <- function(var) {
englue("Column: {{ var }}")
}
my_function(some_column)
#> [1] "Column: some_column"Advanced: Manual Expression Transformation
进阶:手动转换表达式
When you need to modify expressions before injection:
r
my_mean <- function(data, var) {
# 1. Defuse
var <- enquo(var)
# 2. Transform: wrap in mean()
wrapped <- expr(mean(!!var, na.rm = TRUE))
# 3. Inject
data |> dplyr::summarise(mean = !!wrapped)
}For multiple arguments:
r
my_mean <- function(.data, ...) {
vars <- enquos(..., .named = TRUE)
# Transform each expression
vars <- purrr::map(vars, ~ expr(mean(!!.x, na.rm = TRUE)))
.data |> dplyr::summarise(!!!vars)
}当需要在注入前修改表达式时:
r
my_mean <- function(data, var) {
# 1. 延迟求值
var <- enquo(var)
# 2. 转换:用mean()包裹
wrapped <- expr(mean(!!var, na.rm = TRUE))
# 3. 注入
data |> dplyr::summarise(mean = !!wrapped)
}处理多个参数的情况:
r
my_mean <- function(.data, ...) {
vars <- enquos(..., .named = TRUE)
# 转换每个表达式
vars <- purrr::map(vars, ~ expr(mean(!!.x, na.rm = TRUE)))
.data |> dplyr::summarise(!!!vars)
}Base R Equivalents
基础R等效实现
| rlang | Base R | Notes |
|---|---|---|
| | bquote uses |
| | substitute returns naked expr, not quosure |
| | Workaround for dots |
| | Only inside bquote |
| | eval_tidy supports .data/.env pronouns |
| rlang | 基础R | 说明 |
|---|---|---|
| | bquote使用 |
| | substitute返回裸表达式,而非Quosure |
| | 处理可变参数的变通方法 |
| bquote中的 | 仅在bquote内生效 |
| | eval_tidy支持.data/.env代词 |
Pitfalls
常见陷阱
{{
on Non-Arguments
{{对非参数使用`{{}}
{{r
undefined{{}}r
undefinedCorrect: var is a function argument
正确用法:var是函数参数
my_fn <- function(var) {{ var }}
my_fn <- function(var) {{ var }}
Problematic: x is not an argument
错误用法:x不是参数
x <- 1
{{ x }} # Returns 1, not the expression
undefinedx <- 1
{{ x }} # 返回1,而非表达式
undefinedOperators Out of Context
操作符在非目标上下文的问题
Outside tidy eval/inject contexts, operators have different meanings:
| Operator | Intended | Outside Context |
|---|---|---|
| Embrace | Double braces (returns value) |
| Inject | Double negation (logical) |
| Splice | Triple negation (logical) |
These fail silently. See the tidy-evaluation skill for details on proper usage contexts.
在tidy eval/inject上下文之外,操作符有不同含义:
| 操作符 | 预期用途 | 非目标上下文含义 |
|---|---|---|
| Embrace | 双大括号(返回值) |
| 注入 | 双重否定(逻辑运算) |
| 拼接 | 三重否定(逻辑运算) |
这些情况会静默失败。有关正确使用场景的详细信息,请参阅tidy-evaluation技能文档。
See Also
另请参阅
- tidy-evaluation: Programming patterns for data-masked functions
- designing-tidy-r-functions: Function API design principles
- rlang-conditions: Error handling with rlang
- tidy-evaluation:数据掩码函数的编程模式
- designing-tidy-r-functions:函数API设计原则
- rlang-conditions:使用rlang进行错误处理
Reference Files
参考文件
- topic-quosure.md - Complete quosure reference
- topic-metaprogramming.md - Advanced transformation patterns
- topic-multiple-columns.md - Multiple columns patterns
- topic-quosure.md - 完整的Quosure参考文档
- topic-metaprogramming.md - 进阶转换模式
- topic-multiple-columns.md - 多列处理模式
Vignettes
小手册
Access detailed rlang documentation via R:
r
undefined通过R访问rlang的详细文档:
r
undefinedDefusing expressions
延迟求值表达式
vignette("topic-defuse", package = "rlang")
vignette("topic-defuse", package = "rlang")
Injection operators
注入操作符
vignette("topic-inject", package = "rlang")
vignette("topic-inject", package = "rlang")
Or browse all vignettes
或浏览所有小手册
browseVignettes("rlang")
undefinedbrowseVignettes("rlang")
undefined