有人知道为什么下面的方法不起作用吗?看起来stringr::str_glue()找不到file_name参数,因为它在purrr::map()的内部.我怎么才能绕过它呢?

library(tidyverse)

fn_render_ce_template <- function(
    template,
    file_name, 
    early_billing_cycle = NA,
    late_billing_cycle = NA,
    early_price = NA,
    late_price = NA,
    price_dollars = NA,
    price_cents = NA,
    year = NA
){
  template %>% 
    map_chr(str_glue) %>% 
    str_squish()
}

# this doesn't work
fn_render_ce_template(
  template = c('{file_name}_1', '{file_name}_2'), 
  file_name = 'test'
)
#> Error:
#> i In index: 1.
#> Caused by error:
#> ! object 'file_name' not found
#> Backtrace:
#>      x
#>   1. +-global fn_render_ce_template(...)
#>   2. | \-template %>% map_chr(str_glue) %>% str_squish()
#>   3. +-stringr::str_squish(.)
#>   4. | +-stringi::stri_trim_both(str_replace_all(string, "\\s+", " "))
#>   5. | \-stringr::str_replace_all(string, "\\s+", " ")
#>   6. |   \-stringr:::check_lengths(string, pattern, replacement)
#>   7. |     \-vctrs::vec_size_common(...)
#>   8. +-purrr::map_chr(., str_glue)
#>   9. | \-purrr:::map_("character", .x, .f, ..., .progress = .progress)
#>  10. |   +-purrr:::with_indexed_errors(...)
#>  11. |   | \-base::withCallingHandlers(...)
#>  12. |   +-purrr:::call_with_cleanup(...)
#>  13. |   \-stringr (local) .f(.x[[i]], ...)
#>  14. |     \-glue::glue(..., .sep = .sep, .envir = .envir)
#>  15. |       \-glue::glue_data(...)
#>  16. +-glue (local) `<fn>`("file_name")
#>  17. | +-.transformer(expr, env) %||% .null
#>  18. | \-glue (local) .transformer(expr, env)
#>  19. |   \-base::eval(parse(text = text, keep.source = FALSE), envir)
#>  20. |     \-base::eval(parse(text = text, keep.source = FALSE), envir)
#>  21. \-base::.handleSimpleError(...)
#>  22.   \-purrr (local) h(simpleError(msg, call))
#>  23.     \-cli::cli_abort(...)
#>  24.       \-rlang::abort(...)

# but this does?
file_name <- 'test'

fn_render_ce_template(
  template = c('{file_name}_1', '{file_name}_2'), 
  file_name = 'test'
)
#> [1] "test_1" "test_2"

创建于2023-12-23,共reprex v2.0.2

推荐答案

定义匿名函数

简短的回答是,这是关于判断变量的环境.你可以通过在map_chr()调用中定义一个匿名函数来解决这个问题:

render_template <- function(template, file_name) {
    template |>
        map_chr(\(x) str_glue(x))
}

这将返回所需的输出:

render_template(
    template = c("{file_name}_1", "{file_name}_2"),
    file_name = "test"
)
# [1] "test_1" "test_2"

解释

要了解为什么会发生这种情况,请查看str_glue()的函数definition:

str_glue <- function(..., .sep = "", .envir = parent.frame()) {
  glue::glue(..., .sep = .sep, .envir = .envir)
}

默认情况下,它会显示在父框架中.当您使用purrr::map_chr()时,它最终会调用purrr::map_(),而父帧不是正确的帧.

您可以让您的函数手动指定帧以了解正在发生的情况:

render_template <- function(template, file_name, stack_frame_num) {
    template |>
        map_chr(\(x) str_glue(x, .envir = rlang::caller_env(stack_frame_num)))
}

如果我们用堆栈帧编号1调用它,就会得到全局变量.

file_name <- "global"
render_template(
    template = "{file_name}_1",
    file_name = "local",
    stack_frame_num = 1
)
# [1] "global_1"

这同样适用于堆栈帧编号为2的情况.但是,如果我们使用堆栈帧编号3调用它:

render_template(
    template = "{file_name}_1",
    file_name = "local",
    stack_frame_num = 3
)
# [1] "local_1"

幸运的是,我们不需要手动传递堆栈帧编号,因为在函数中定义匿名函数意味着父帧是正确的,但希望这能让您了解为什么会发生这种行为.

R相关问答推荐

如何使用geom_sf在边界显示两种 colored颜色 ?

在特定列上滞后n行,同时扩展框架的长度

是否可以 Select 安装不带文档的R包以更有效地存储?

无法在我的情节中表现出显着的差异

如何使用rmarkdown和kableExtra删除包含折叠行的表的第一列的名称

保存包含循环和ifelse的函数的输出

将复杂的组合列表转换为数据框架

在数学中正确显示摄氏度、开氏度或华氏度

derrr mutate case_when grepl不能在R中正确返回值

计算具有奇数日期的运行金额

计算时间段的ECDF(R)

根据日期从参考帧中创建不同的帧

在df中保留原始变量和新变量

展开对数比例绘图的轴(添加填充)

为左表中的所有行使用值Fill滚动左连接

汇总数据的Sheffe检验的P值(平均值和标准差)

在r中整理图例和堆叠图的问题

名字的模糊匹配

在不带max()的data.table中按组查找最后一个元素

创建两个变量组合的索引矩阵