如何向bench::mark的exprs参数传递批量生成的表达式?
如何自动生成适配bench::mark的表达式列表?
问题背景
首先定义一个包含多个函数的列表:
funcs <- list( loop = function(n) { result <- 0 for (i in seq_len(n)) { result <- result + i } result }, vect = function(n) sum(seq_len(n)) )
手动编写每个函数的调用表达式传入bench::mark可以正常运行:
N <- 1e6 bench::mark(funcs$loop(N), funcs$vect(N)) # 运行正常
但这种手动方式存在两个明显问题:
- 新增函数时需手动添加对应调用表达式,容易遗漏;
- 重复书写函数调用的参数部分,存在冗余。
尝试通过生成表达式列表传入exprs参数时出现报错:
bench::mark(exprs = lapply(funcs, \(f) expression(f(1e6)))) # 报错:Error in f(1e+06) : could not find function "f"
请问如何生成适配bench::mark的exprs参数的表达式列表?
解决方案
要解决这个问题,核心是生成指向列表中具体函数的调用表达式,而非引用循环中的临时变量。以下是几种可行的实现方式:
方法1:使用bquote()生成表达式
bquote()可以保留表达式中的变量引用,通过.指代当前环境中的变量,直接生成目标调用表达式:
N <- 1e6 # 遍历函数列表名称,生成对应调用表达式 expr_list <- lapply(names(funcs), function(name) { bquote(funcs[[.(name)]](.(N))) }) # 给表达式列表命名,方便bench结果识别函数 names(expr_list) <- names(funcs) bench::mark(exprs = expr_list)
方法2:手动构造调用表达式
使用call()和call2()函数直接构造函数调用结构,明确指定函数位置与参数:
N <- 1e6 expr_list <- lapply(names(funcs), function(name) { # 先构造 funcs[[name]] 的表达式,再构造调用该函数的表达式 call("[[", quote(funcs), name) |> call2(N) }) names(expr_list) <- names(funcs) bench::mark(exprs = expr_list)
方法3:用purrr::imap()简化代码
如果使用purrr包,imap()可以同时遍历函数列表的元素和名称,代码更简洁:
library(purrr) N <- 1e6 expr_list <- imap(funcs, ~bquote(funcs[[.(.y)]](.(N)))) bench::mark(exprs = expr_list)
报错原因说明
之前的代码生成的表达式是f(1e6),其中f是lapply循环内的临时变量。当bench::mark执行这些表达式时,会在全局环境中查找f,但全局环境中不存在该函数,因此触发报错。而上述方法均直接生成funcs[["loop"]](1e6)这类具体的调用表达式,避免了引用临时变量的问题。
内容的提问来源于stack exchange,提问作者banbh
相关产品推荐
相关产品推荐

