如何评估嵌套函数中的胶水表达式?

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)
}

但现在首选粘合语法。