R元编程实现列表推导:动态生成函数参数的问题求助
R元编程实现动态列表推导的解决方案
这是一个为深入学习R元编程设计的练手项目,目标是实现类似Haskell或Python的列表推导功能。现有代码参考管道操作符实现,但存在两个问题:硬编码函数参数、传入多余变量时报错。
现有代码及问题
library(rlang) `%c|%` <- function(lhs, rhs){ lhs <- (substitute(lhs)) rhs <- (substitute(rhs)) if (rhs[[1L]] == ".") rhs[[1]] = quote(list) e <- expr(purrr::pmap(!!rhs, \(x, y) !!lhs)) eval(e, envir = parent.frame()) } # 基础调用符合预期 c(x, y) %c|% .(x = 1:2, y = 10:11) # 返回结果: # [[1]] # [1] 1 10 # # [[2]] # [1] 2 11
存在的问题:
- 函数内部硬编码了
\(x, y),无法根据右侧命名参数动态生成对应参数列表 - 传入多余变量时会触发未使用参数错误:
> c(x, y) %c|% .(x = 1:2, y = 10:11, z = letters[1:2]) Error in `purrr::pmap()`: ℹ In index: 1. Caused by error in `.f()`: ! unused argument (z = .l[[3]][[i]])
解决代码
library(rlang) `%c|%` <- function(lhs, rhs) { lhs_expr <- substitute(lhs) rhs_expr <- substitute(rhs) # 将.()语法替换为标准list()调用 if (rhs_expr[[1L]] == quote(.)) { rhs_expr[[1]] <- quote(list) } # 提取右侧所有命名参数的名称 param_names <- names(rhs_expr[-1]) # 将参数名转换为R符号对象 params <- syms(param_names) # 动态生成lambda函数:参数为所有右侧命名参数,执行左侧表达式 lambda <- expr(\(!!!params) !!lhs_expr) # 构建pmap调用的表达式 call_expr <- expr(purrr::pmap(!!rhs_expr, !!lambda)) # 在父环境中执行生成的表达式 eval(call_expr, envir = parent.frame()) }
测试验证
- 基础功能验证(结果与原代码一致):
c(x, y) %c|% .(x = 1:2, y = 10:11) # [[1]] # [1] 1 10 # # [[2]] # [1] 2 11
- 多余参数验证(无报错,正常返回结果):
c(x, y) %c|% .(x = 1:2, y = 10:11, z = letters[1:2]) # [[1]] # [1] 1 10 # # [[2]] # [1] 2 11
关键逻辑说明
- 使用
substitute()捕获左右两侧的原始表达式,避免提前求值 - 通过
names(rhs_expr[-1])提取右侧所有命名参数的名称,用syms()转换为R符号 - 利用rlang的非标准求值语法
!!!(unquote-splice),将动态生成的参数列表注入lambda函数 - 生成的lambda函数包含所有右侧参数,因此
purrr::pmap传递所有参数时不会触发未使用参数错误
内容的提问来源于stack exchange,提问作者gabagool
相关产品推荐
相关产品推荐

