在 R 中模拟动态作用域以按任意函数进行过滤

Emulate dynamic scoping in R to filter by arbitrary functions

我在一个目录下有很多文件,我想根据它们的内容是否满足某个条件,将它们分配到两个新创建的子目录中。而且,我需要对多个目录执行这个操作,而且每次的条件可能都不一样。

我想我会这样做的方法是创建一个读取文件的高阶函数,从每个文件中提取感兴趣的 属性,然后应用条件(作为参数提供以函数的形式)逐元素到结果向量的值:

filter.by.property <- function(where, funct) {
  setwd(where)
  paths <- list.files(".", pattern = ".file")
  Robjects <- Map(read.file, paths)
  values <- sapply(Robjects, function(x) property_of_interest(x))
  passes <- Filter(funct, paths)
  dir.create("dir1")
  dir.create("dir2")
  file.copy(passes, paste("./dir1/", 1:length(passes), ".file", sep = ""))
  file.copy(paths[!paths %in% passes], paste("./dir2/", (length(passes) + 1):length(paths), ".file", sep = ""))
}

问题是我希望条件指定函数(在 funct 参数中提供)能够访问 values 向量。例如:

ten.biggest <- function(path) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(values), 10)
}

filter.by.property(<place>, ten.biggest)

由于 R 是词法范围的,它会在定义 ten.biggest 的环境(全局环境)中查找 values,而不是在调用它的环境中(即在 filter.by.property).我可以通过在 filter.by.property 中对 values 使用全局赋值来解决这个问题,但除非绝对必要,否则我宁愿不这样做。我也可以提供 values 作为 ten.biggest 的另一个参数,但这样函数将不再是一元的,我不确定它如何(甚至是否)仍然可以在 [=22] 中使用=].

我尝试的一个解决方案是重写 ten.biggest 如下

ten.biggest <- function(path) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(eval.parent(parse(text = values))), 10)
}

但这没有用。

有什么方法可以在 R 中模拟动态作用域来完成这项工作吗?通过阅读相关的 StackExchange 问题,似乎 body 函数可能有所帮助,但我不知道如何将它应用到我的问题中。

我找到了一个不依赖切换环境的解决方案。我允许 ten.biggest 接受两个参数,并在 filter.by.property 内部定义了一个辅助函数 outer_fxn,通过设置其参数之一将二元函数转换为一元函数。然后二进制 ten.biggest 函数可以通过后者的 funct 参数传递给 filter.by.propertyten.biggestvals 参数被设置为 values outer_fxn,然后可以在 Filter:

中使用生成的一元函数
ten.biggest <- function(path, vals) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(vals), 10)
}

filter.by.property <- function(where, funct) {
  setwd(where)
  paths <- list.files(".", pattern = ".file")
  Robjects <- Map(read.file, paths)
  values <- sapply(Robjects, function(x) property_of_interest(x))
  outer_fxn <- function(x) { funct(x, values) }
  passes <- Filter(outer_fxn, paths)
  dir.create("dir1")
  dir.create("dir2")
  file.copy(passes, paste("./dir1/", 1:length(passes), ".file", sep = ""))
  file.copy(paths[!paths %in% passes], paste("./dir2/", (length(passes) + 1):length(paths), ".file", sep = ""))
}