subset_base是 r 基函数的简单形式subset()。该示例取自 Advanced R 第 20.6.1 章。
这个函数本身工作得很好:
subset_base <- function(data, rows) {
rows <- substitute(rows)
rows_val <- eval(rows, data, parent.frame())
data[rows_val, , drop = FALSE]
}
my_df <- data.frame(x = 1:3)
subset_base(my_df, x == 1)
#> x
#> 1 1
Run Code Online (Sandbox Code Playgroud)
然而,当我们构建一个包装函数时apply_subset,在这个包装函数中定义一些值并在提供参数时zzz调用(在同一函数中)找不到它。lapply()subset_basezzz
我想更好地理解为什么zzz找不到。我的思维模型如下: subset_base被调用,lapply被调用apply_subset。在subset_base我们内部评估rows所提供的参数,data.frame data该参数会自动转换为环境。该环境由调用环境封装parent.frame()。这应该是lapply. 我认为这个环境再次有lapply作为其父级的调用环境。这将是 的执行环境apply_subset。但事实似乎并非如此,因为如果这是真的,zzz就应该被发现。
apply_subset <- function(){
zzz <- 2
dfs <- list(data.frame(x = 1:3), data.frame(x = 4:6))
lapply(dfs, FUN = subset_base, x == zzz)
}
apply_subset()
#> Error in eval(rows, data, parent.frame()): object 'zzz' not found
Run Code Online (Sandbox Code Playgroud)
lapply更奇怪的是,当我们从subset_base直接提供对象/函数名称改为调用时,将其包装到匿名函数中时,zzz可以发现。
apply_subset2 <- function(){
zzz <- 2
dfs <- list(data.frame(x = 1:3), data.frame(x = 4:6))
lapply(dfs, FUN = \(df) subset_base(df, x == zzz))
}
apply_subset2()
#> [[1]]
#> x
#> 2 2
#>
#> [[2]]
#> [1] x
#> <0 rows> (or 0-length row.names)
Run Code Online (Sandbox Code Playgroud)
创建于 2024-01-27,使用reprex v2.0.2
我非常感谢对以下问题的解释:(1)调用堆栈中每个执行环境的父环境是什么,(2)为什么我的假设它们对齐显然是错误的,以及(3)为什么当调用时这种情况会发生变化tolapply使用匿名函数。此外,很高兴知道(4)是否有任何方法可以在不使用匿名函数的情况下调用 lapply(Advanced R 似乎建议(也许)唯一的解决方案是使用 rlangs quosures)。
关键点是,如果我们相对于父框架计算表达式,它将查看调用堆栈的一层,如果没有找到,将不会进一步查看调用堆栈,而是会查看定义调用者的环境并递归地通过它的祖先。
有了这个背景我们就可以回答以下问题:
(1)调用栈中各个执行环境的父环境是什么,
当apply_subset运行时,函数subset_base被传递到lapply,然后内部的代码lapply调用传递的代码subset_base,因此内部的执行环境lapply是父框架,而不是所在的subset_base执行环境。 不在执行环境中,因此它查找的下一个位置是定义位置的父环境(不是调用堆栈的更上方)。在这种情况下,它会在基础包中查找,但也不在那里,然后它会查找全局环境和搜索路径上的所有包,但不在那里,因此会出现错误。apply_subsetzzzzzzlapplylapplylapplyzzzzzz
(2) 为什么我关于它们对齐的假设显然是错误的,以及
往上看。
(3) 当调用 lapply 使用匿名函数时,为什么会发生这种变化。此外,很高兴知道,如果
当匿名函数调用时,它会在匿名函数的执行环境中subset_base查找,但由于它不在那里,因此它会查找匿名函数的父环境(而不是调用堆栈的更上方),并且由于匿名函数是在父环境中定义的找到where的执行环境。zzzapply_subsetapply_subsetzzz
(4) 有什么方法可以在不使用匿名函数的情况下使对 lapply 的调用起作用
推广到其他情况的方法是通过envir=像这样的显式参数来传递环境:
subset_base <- function(data, rows, envir = parent.frame()) { ##
rows <- substitute(rows)
rows_val <- eval(rows, data, envir) ##
data[rows_val, , drop = FALSE]
}
apply_subset <- function(){
zzz <- 2
dfs <- list(data.frame(x = 1:3), data.frame(x = 4:6))
lapply(dfs, FUN = subset_base, x == zzz, envir = environment()) ##
}
apply_subset()
Run Code Online (Sandbox Code Playgroud)
给予
[[1]]
x
2 2
[[2]]
[1] x
<0 rows> (or 0-length row.names)
Run Code Online (Sandbox Code Playgroud)