2

我正在阅读Advanced R一书,对非标准评估一章中反复提到的“逃生舱”概念感到困惑。比如作者第一次提到这个词,它有如下定义:

作为开发人员,您应该始终提供一个逃生舱口:使用标准评估的函数的替代版本。

它也有一些关于逃生舱口的例子。其中一个示例来自Calling from another function部分。作者说:

通常,当函数被用户直接调用时,语言上的计算最有用,而当它们被其他函数调用时,用处不大。

请参见下面示例的代码:

sample_df <- data.frame(a = 1:5, b = 5:1, c = c(5, 3, 1, 4, 1))

subset2 <- function(x, condition) { 
condition_call <- substitute(condition)
  r <- eval(condition_call, x, parent.frame())
  x[r, ]
}

scramble <- function(x) x[sample(nrow(x)), ]

subscramble <- function(x, condition) {
  scramble(subset2(x, condition))
}

但它不起作用:

subscramble(sample_df, a >= 4)
# Error in eval(expr, envir, enclos) : object 'a' not found
traceback()
#> 5: eval(expr, envir, enclos)
#> 4: eval(condition_call, x, parent.frame()) at #3
#> 3: subset2(x, condition) at #1
#> 2: scramble(subset2(x, condition)) at #2
#> 1: subscramble(sample_df, a >= 4)

作者说我们可以编写一个版本的子集2(),在这种情况下采用已经引用的表达式。代码如下所示:

subset2_q <- function(x, condition) {
  r <- eval(condition, x, parent.frame())
  x[r, ]
}
subset2 <- function(x, condition) {
  subset2_q(x, substitute(condition))
}

subscramble <- function(x, condition) {
  condition <- substitute(condition)
  scramble(subset2_q(x, condition))
}

然后它运行良好:

subscramble(sample_df, a >= 3)
#>   a b c
#> 4 4 2 4
#> 5 5 1 1
#> 3 3 3 1
subscramble(sample_df, a >= 3)
#>   a b c
#> 5 5 1 1
#> 3 3 3 1
#> 4 4 2 4

尽管作者给了我例子,但我仍然不明白逃生舱口。那么,有人可以在本书或 R 编程语言中解释它的定义吗?我的会话信息:

sessionInfo()
R version 3.5.0 (2018-04-23)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=Chinese (Simplified)_People's Republic of China.936 
[2] LC_CTYPE=Chinese (Simplified)_People's Republic of China.936   
[3] LC_MONETARY=Chinese (Simplified)_People's Republic of China.936
[4] LC_NUMERIC=C                                                   
[5] LC_TIME=Chinese (Simplified)_People's Republic of China.936    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_3.5.0 tools_3.5.0    yaml_2.2.0
4

1 回答 1

0

在这一点上, Advanced R中关于非标准评估 (NSE) 的章节有点过时。它所指的“逃生舱”如您的问题中所定义:“使用标准评估的功能的替代版本”。如果您查看 的历史dplyr,您会注意到 和 之类的函数,它们实际上是 和 的标准评估 (SE)select_版本。此后,这些功能已被弃用,维护两个功能版本(一个 SE 和一个 NSE)的整个概念也不再受欢迎。NSE 的现代方法及其在中的使用在最近的关于整洁评估的书中有所介绍。filter_selectfilterdplyr

您的代码不工作的原因是因为substitute()在嵌套函数中不能正常工作。在你的情况下:

## Works as intended
scramble(subset2( sample_df, a >= 4 ))

## Does essentially the same thing but placed within a function
## Fails due to substitute() being inside a now-nested function
subscramble( sample_df, a >= 4 )

要修复这些函数,您可以简单地从substitute()to切换rlang::enexpr()并使用quasiquotationcondition在整个嵌套函数中传递表达式。enquo()请注意,在这种情况下您不需要;enquo()是一个版本enexpr(),它还捕获要评估表达式的环境。在您的情况下,该环境是数据框,它已经被传递给函数x

subset2 <- function(x, condition) {
  condition_call <- rlang::enexpr(condition)
  r <- eval(condition_call, x, parent.frame())
  x[r, ]
}

subscramble <- function(x, condition) {
  scramble(subset2(x, !!rlang::enexpr(condition)))
}

subscramble( sample_df, a >= 4 )   ## Now works as intended
于 2019-09-18T03:31:14.910 回答