列表中的 rlang 表达式作为函数的参数

rlang Expressions in List as Args to Function

我正在尝试将用户输入解析为函数调用的参数(在表达式中)。好像我很接近但是!将我的论点包装在括号中,这是行不通的。我正在尝试使用用户输入重新创建以下内容:

recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>% 
  step_log(disp, base = 10, offset = 0) %>% 
  prep()
library(rlang)
library(tidymodels)

user_dv <- "mpg"
user_idv <- c("cyl", "hp", "wt", "disp")

user_step <- "log"
user_selector <- "disp"
user_args <- "base = 10, offset = 0"

formula <- as.formula(paste(user_dv, paste(user_idv, collapse = " + "), sep = " ~ "))

rcp <- expr(recipe(!!formula,data = mtcars))

add_step <- function(x, step, selector, args){
  f <- parse_expr(paste0("step_", step))
  vars <- ensym(user_selector)
  
  args <- args %>% 
    str_replace(",", ";") %>% 
    parse_exprs()

  step_expr <- call2(f, vars, !!!args)
  
  expr(!!x %>% !!step_expr)
  
}

rcp %>% 
  add_step(user_step, user_selector, user_args) %>% 
  eval() %>% 
  prep()

我的表情最终变成了这样:

recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>% 
      step_log(disp, (base = 10), (offset = 0))

哪个不准备()

parse_exprs() 之后,您最终得到存储在未命名列表中的赋值表达式:

# [[1]]
# base = 10
#
# [[2]]
# offset = 0

同时,要使用 !!! 获得您想要的效果,它们需要是命名列表中的值:

# $base
# [1] 10
#
# $offset
# [1] 0

话虽如此,我建议改为“转发点”,因为它会导致更简单、更灵活的实施:

add_step <- function(x, step, selector, ...){
  f <- parse_expr(paste0("step_", step))
  vars <- sym(selector)
  step_expr <- call2(f, vars, ...)      # <---- forwarding the dots here

  expr(!!x %>% !!step_expr)
}

用户现在可以简单地直接向 add_step() 提供所需的参数:

rcp %>% add_step(user_step, user_selector, base=10, offset=0)

# recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>% 
#    step_log(disp, base = 10, offset = 0)

或者将它们存储在列表中并在你的函数上使用!!!

user_args <- list(base = 10, offset=0)
rcp %>% add_step(user_step, user_selector, !!!user_args)