使用 bquote 时,R returnValue 在 on.exit 中不起作用

R returnValue not working in on.exit when using bquote

在使用新的 R 3.2.0 函数时 returnValue 我发现了一种行为,即使不是错误,也可能是不需要的行为。

好像returnValue包裹在eval(bquote(on.exit(里就不行了。见下文。

  1. 按应有的方式工作 - 不使用 eval(bquote(on.exit(

f <- function(x, case = 0L){
  pt <- proc.time()[[3L]]
  on.exit(message(paste("elapsed:",round(proc.time()[[3L]]-pt,4),"\nclass:",class(returnValue())[1L])))
  Sys.sleep(0.001)
  if(case==1L) rcol <- "a"
  else if(case==2L) rcol <- "b"
  else if(case==3L){
    pt <- 100
    x$a <- x$a + pt
    rcol <- "a"
  } else return(x)
  return(x[[rcol]])
}
df <- data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = FALSE)
f(df, case=0L) # OK: expected
# elapsed: 0.001 
# class: data.frame
#   a b
# 1 1 a
# 2 2 b
# 3 3 c
f(df, case=1L) # OK: expected
# elapsed: 0.002 
# class: integer
# [1] 1 2 3
f(df, case=2L) # OK: expected
# elapsed: 0.002 
# class: character
# [1] "a" "b" "c"
f(df, case=3L) # HMMM?: not expected but still correct
# elapsed: 589.695 
# class: numeric
# [1] 101 102 103

case=3L 上,我们可能会得到意想不到的结果,因为 pt 变量在处理过程中被覆盖了。这仍然不是问题,因为它可以正常工作。然而,我们希望在不依赖变量名称的情况下处理此类情况,这就是为什么在下面使用 bquote

  1. returnValue 如果包装到 eval(bquote(on.exit(
  2. 中,总是返回 NULL

f <- function(x, case = 0L){
  pt <- proc.time()[[3L]]
  eval(bquote(on.exit(message(paste("elapsed:",round(proc.time()[[3L]]-.(pt),4),"\nclass:",class(returnValue())[1L])))))
  Sys.sleep(0.001)
  if(case==1L) rcol <- "a"
  else if(case==2L) rcol <- "b"
  else if(case==3L){
    pt <- 100
    x$a <- x$a + pt
    rcol <- "a"
  } else return(x)
  return(x[[rcol]])
}
df <- data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = FALSE)
f(df, case=0L)
# elapsed: 0.002 
# class: NULL
#   a b
# 1 1 a
# 2 2 b
# 3 3 c
f(df, case=1L)
# elapsed: 0 
# class: NULL
# [1] 1 2 3
f(df, case=2L)
# elapsed: NULL 
# class: character
# [1] "a" "b" "c"
f(df, case=3L)
# elapsed: 0.001 
# class: NULL
# [1] 101 102 103

我认为它不应该那样工作。
如何避免此类行为并在 on.exit?

中未计算的表达式中替换值

on.exit() 只是延迟表达式评估。它在创建时不存储 pt 的值,因为它在 运行 时未被计算。如果你想 "protect" 一个值,你需要创建你自己的范围。一个函数可以有它自己的作用域。例如:

f <- function(x, case = 0L){
  eptime <- local({start = proc.time()[[3L]]; function() proc.time()[[3L]]-start})
  on.exit(message(paste("elapsed:",round(eptime(),4),"\nclass:",class(returnValue())[1L])))
  ...
}

所以这里我们用一个名为eptime.

的函数替换函数变量pt

eval(bquote()) 强制立即执行 returnValue(),而不是在函数结束时。你没有得到你想要的延迟评估。见

f <- function(x) {
    eval(bquote(on.exit(print(paste("exit", .(x))))))
    x<-"ok"
    print(paste("return",x))
}
f("apple")
# [1] "exit apple"
# [1] "return ok"

这样您的退出代码 运行 首先。这并非 returnValue() 所独有。