在data.table的j表达式中使用str_glue出现环境错误的原因咨询
这个问题的核心是data.table对j表达式的特殊求值逻辑和str_glue默认的环境查找规则之间的冲突,咱们一步步拆解:
1. data.table的j表达式是在特殊环境里运行的
当你执行dt[, cr := explain_formula(mpg, conversion_rates)]时,data.table并不会直接在全局环境里执行j表达式,而是会创建一个临时的求值环境(咱们暂时叫它DT_env):
- 这个环境直接绑定了data.table的所有列(比如这里的
mpg、rn等); - 它的父环境是你调用
dt[, ...]的环境(这里是全局环境),而不是explain_formula函数的执行环境。
2. str_glue默认的环境查找没指向函数内部
str_glue默认用envir = parent.frame(),这个参数的意思是找调用str_glue的那个函数的父环境。在你的代码里,str_glue是被purrr::map_chr的匿名回调函数调用的:
- 当你单独测试
explain_formula时,这个匿名回调的父环境是explain_formula的执行环境,所以str_glue能找到cri变量; - 但在data.table的场景下,整个
explain_formula(mpg, conversion_rates)是在DT_env里被调用的,所以匿名回调的父环境链最终指向了DT_env——而DT_env里根本没有cri这个变量,自然就报错了。
验证这个结论
你可以在explain_formula里加一行代码,打印parent.frame()的内容,就能直观看到差异:
explain_formula = function(mpg, cr) { print("当前函数环境的变量:") print(ls(envir = environment())) # 会输出mpg、cr、cri print("str_glue默认查找的环境变量:") print(ls(envir = parent.frame())) # data.table场景下,这里是data.table的列名,没有cri cri = cr$mpg_lkm result = paste("lkm obtained by dividing {cri} by", mpg) result = purrr::map_chr(result, stringr::str_glue) return(result) }
运行dt[, cr := explain_formula(mpg, conversion_rates)],第二个print的结果会是data.table的列名列表,完全看不到cri。
解决方法:强制str_glue在函数内部环境查找
正如你已经发现的,解决方法就是显式告诉str_glue,要在explain_formula的执行环境里找变量,也就是传递.envir = environment():
explain_formula = function(mpg, cr) { cri = cr$mpg_lkm result = paste("lkm obtained by dividing {cri} by", mpg) # 显式指定环境为当前函数的执行环境 result = purrr::map_chr(result, ~stringr::str_glue(.x, .envir = environment())) return(result) }
这样str_glue就会直接去explain_formula的内部找cri,完全不受data.table的特殊环境影响。
另外补充一句:如果你的stringr版本足够新,其实str_glue已经支持向量化了,不需要用purrr::map_chr,直接写result = stringr::str_glue(result, .envir = environment())就行。
内容的提问来源于stack exchange,提问作者jayke

