如何评估嵌套函数中的胶水表达式?
How to evaluate a glue expression in a nested function?
我正在尝试嵌套一个将两个字符串粘合在一起的函数,该函数使用组合的字符串来命名数据框的列。但是,问题似乎是胶水表达式没有足够早地评估为字符串。在将表达式作为参数传递给另一个函数之前,我可以(并且应该)强制对其求值吗?
library(tidyverse)
# define inner function
add_prefix <- function(string) {
x <- glue::glue("prefix_{string}")
return(as.character(x))
}
# define outer function
mod_mtcars <- function(df, name) {
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
#> Error: The LHS of `:=` must be a string or a symbol
# alternative outer function with explicit enquoting
mod_mtcars2 <- function(df, name) {
name <- ensym(name)
df %>%
mutate(!!name := mpg ^ 2)
}
mod_mtcars2(mtcars, add_prefix("foo"))
#> Error: Only strings can be converted to symbols
由 reprex package (v2.0.1)
于 2021-10-30 创建
name
不是符号。尝试:
mod_mtcars <- function(df, name) {
name <- sym(name)
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
更好的是,tidy eval 现在支持 glue strings 因此您可以简化为:
# don't need add_prefix()
mod_mtcars <- function(df, name){
df %>%
mutate("prefix_{{name}}" := mpg ^ 2)
}
mod_mtcars(mtcars, foo)
最后,curly-curly 运算符会隧道化您的表达式,您正在向它传递一个字符串。如果您保留当前函数,请改用 bang-bang 运算符:
mod_mtcars <- function(df, name){
df %>%
mutate(!! name := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
我们可能需要对输入进行转义(保持OP原有功能不变)
mod_mtcars(mtcars, !!add_prefix("foo"))
-输出
mpg cyl disp hp drat wt qsec vs am gear carb prefix_foo
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 441.00
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 441.00
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 519.84
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 457.96
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 349.69
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 327.61
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 204.49
...
首先,与直接在 LHS 中包含相比,胶合字符串语法现在更受欢迎。所以更喜欢 "{{ var }}" := expr
而不是 {{ var }} := expr
。在 rlang 的未来版本(明年)中,我们将可以使用带有 =
的粘合字符串。到那时,:=
将几乎被取代。在添加胶水支持之前,我们使用 :=
允许 !!
在 LHS 上注入。
其次,您的问题是您使用的是 {{
而不是简单的注入。 {{
用于注入作为参数提供的 expression,而不是表达式的 value。使用带有 "{"
的普通胶水插值来注入值:
mod_mtcars <- function(df, name) {
df %>%
mutate("{name}" := mpg ^ 2)
}
PS:您的!!
版本也有类似的问题。因为您在参数上使用了 ensym()
,所以您是在消除作为参数提供的表达式而不是使用该值。但是 ensym()
要求表达式是一个简单的名称,而您提供了完整的计算,从而导致错误。您可以这样修复它:
mod_mtcars2 <- function(df, name) {
df %>%
mutate(!!name := mpg ^ 2)
}
但现在首选粘合语法。
我正在尝试嵌套一个将两个字符串粘合在一起的函数,该函数使用组合的字符串来命名数据框的列。但是,问题似乎是胶水表达式没有足够早地评估为字符串。在将表达式作为参数传递给另一个函数之前,我可以(并且应该)强制对其求值吗?
library(tidyverse)
# define inner function
add_prefix <- function(string) {
x <- glue::glue("prefix_{string}")
return(as.character(x))
}
# define outer function
mod_mtcars <- function(df, name) {
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
#> Error: The LHS of `:=` must be a string or a symbol
# alternative outer function with explicit enquoting
mod_mtcars2 <- function(df, name) {
name <- ensym(name)
df %>%
mutate(!!name := mpg ^ 2)
}
mod_mtcars2(mtcars, add_prefix("foo"))
#> Error: Only strings can be converted to symbols
由 reprex package (v2.0.1)
于 2021-10-30 创建name
不是符号。尝试:
mod_mtcars <- function(df, name) {
name <- sym(name)
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
更好的是,tidy eval 现在支持 glue strings 因此您可以简化为:
# don't need add_prefix()
mod_mtcars <- function(df, name){
df %>%
mutate("prefix_{{name}}" := mpg ^ 2)
}
mod_mtcars(mtcars, foo)
最后,curly-curly 运算符会隧道化您的表达式,您正在向它传递一个字符串。如果您保留当前函数,请改用 bang-bang 运算符:
mod_mtcars <- function(df, name){
df %>%
mutate(!! name := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
我们可能需要对输入进行转义(保持OP原有功能不变)
mod_mtcars(mtcars, !!add_prefix("foo"))
-输出
mpg cyl disp hp drat wt qsec vs am gear carb prefix_foo
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 441.00
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 441.00
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 519.84
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 457.96
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 349.69
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 327.61
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 204.49
...
首先,与直接在 LHS 中包含相比,胶合字符串语法现在更受欢迎。所以更喜欢 "{{ var }}" := expr
而不是 {{ var }} := expr
。在 rlang 的未来版本(明年)中,我们将可以使用带有 =
的粘合字符串。到那时,:=
将几乎被取代。在添加胶水支持之前,我们使用 :=
允许 !!
在 LHS 上注入。
其次,您的问题是您使用的是 {{
而不是简单的注入。 {{
用于注入作为参数提供的 expression,而不是表达式的 value。使用带有 "{"
的普通胶水插值来注入值:
mod_mtcars <- function(df, name) {
df %>%
mutate("{name}" := mpg ^ 2)
}
PS:您的!!
版本也有类似的问题。因为您在参数上使用了 ensym()
,所以您是在消除作为参数提供的表达式而不是使用该值。但是 ensym()
要求表达式是一个简单的名称,而您提供了完整的计算,从而导致错误。您可以这样修复它:
mod_mtcars2 <- function(df, name) {
df %>%
mutate(!!name := mpg ^ 2)
}
但现在首选粘合语法。