将粘合函数嵌套在自定义函数中。

3
我想创建一个自定义的日志函数,可以在其他函数中使用。我在自定义函数中遇到了问题,参数似乎无法流动到内部的日志函数中。我的自定义日志函数是受logger包启发的,但我计划进一步扩展其用途(因此logger不太符合我的需求)。
log_fc <- function(type = c("INFO", "ERROR"), ...) {
  
  print(
    glue::glue("[{type} {Sys.time()}] ", ...)
  )
  
}

接下来我打算在各种其他自定义函数中使用log_fc,以下是一个例子:

test_fc <- function(forecast) {

  log_fc(type = "INFO", "{forecast} is here")
  
  #print(forecast)
}

如果我测试这个,会得到以下错误:

> test_fc(forecast = "d")
 Error in eval(parse(text = text, keep.source = FALSE), envir) : 
object 'forecast' not found

我不确定为什么参数“forecast”没有被内部的test_fc函数捕获。谢谢。

4个回答

2

有两件事情正在发生。

首先,名称forecast从未传递给log_fcpaste解决方案从不需要名称,它只需要值,因此仍然有效。 您需要类似于

log_fc(type = "INFO", "{forecast} is here", forecast = forecast)

第一个问题很简单,只需要将名称放入log_fc中即可。

第二个问题比较复杂。这是许多tidyverse函数的设计决策。它们希望能够编写像f(x = 3, y = x + 1)这样的代码,其中第二个参数中的x获取绑定到第一个参数中的值。

标准的R评估规则不会这样做;它们会在调用f的环境中查找x,因此f(y = x + 1, x = 3)将在函数中绑定相同的值,就像以另一种顺序放置参数一样。

tidyverse实现这种非标准评估会破坏R对...的内部处理。解决方法(在此处描述:https://github.com/tidyverse/glue/issues/231)是告诉glue()在特定位置评估参数。您需要更改日志函数以解决此问题。

下面展示了一种可能的更改。我认为@Waldi的更改实际上更好,但我会保留这个更改以展示不同的方法。

log_fc <- function(type = c("INFO", "ERROR"), ...) {
  # Get all the arguments from ...
  args <- list(...)
  
  # The unnamed ones are messages, the named ones are substitutions
  named <- which(names(args) != "")
  
  # Put the named ones in their own environment
  e <- list2env(args[named])
  
  # Evaluate the substitutions in the new env
  print(
    glue::glue("[{type} {Sys.time()}] ", ..., .envir = e)
  )
}

test_fc <- function(forecast) {
  
  log_fc(type = "INFO", "{forecast} is here", forecast = forecast)

  }


test_fc(forecast = "d")
#> [INFO 2022-12-18 06:25:29] d is here

使用reprex v2.0.2于2022年12月18日创建


user2554330 谢谢,我已经采用了 @Waldi 的答案,但是你的评论给了我很多启示。 - sactyr

2
你可以使用.envir参数:
log_fc <- function(type = c("INFO", "ERROR"), ...) {
  env <- new.env(parent=parent.frame())
  assign("type",type,env)
  print(
    glue::glue("[{type} {Sys.time()}] ", ...,.envir = env)
  )
}


  
test_fc <- function(forecast) {
    
    log_fc(type = "INFO", "{forecast} is here")
    
}

  
test_fc("My forecast")
#> [INFO 2022-12-18 12:44:11] My forecast is here

这很不错,但它忽略了“类型”规范,并打印了两次该消息。有没有办法解决这个问题? - user2554330
看我的编辑:将“type”分配给“env”。 - Waldi
1
很危险:它修改了‘test_fc’本地变量。我会这样做env <- new.env(parent = parent.frame()); env$type <- type. - user2554330
@user2554330,感谢您的评论,我已经相应地更新了我的帖子。 - Waldi

0
这是因为当你的test_fc函数连接到log_fc函数时,forecats变量将无法找到,因为它不是全局函数;因此,你无法从其他函数中访问它。
解决方法是定义一个全局变量:
log_fc <- function(type = c("INFO", "ERROR"), ...) {
  
  print(
    glue::glue("[{type} {Sys.time()}] ", ...)
  )
  
}

test_fc <- function(forecast) {

  forecast <<- forecast
  log_fc(type = "INFO", "{forecast} is here")
 
}

print(test_fc(forecast = "d"))

输出:

d is here

当定义全局变量时,似乎确实可以工作,但我能问一下为什么使用paste(而不是glue)时我们不需要一个全局变量吗?即log_fc_print <- function(type = c("INFO", "ERROR"), ...) { print( paste("[", type, Sys.time(), "]", ...) ) } test_fc_print <- function(forecast) { log_fc_print(type = "INFO", paste(forecast, "is here")) } - sactyr

0

由于您已经在使用glue,因此可以在test_fc中使用另一个glue :: glue来实现传递,例如:

log_fc <- function(type = c("INFO", "ERROR"), ...) {
  print(
    glue::glue("[{type} {Sys.time()}] ", ...)
  )
}

test_fc <- function(forecast) {
  log_fc(type = "INFO", glue::glue("{forecast} is here"))
}

它产生

> test_fc('arctic blast')
[INFO 2022-12-21 15:56:18] arctic blast is here
>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接