0

编辑:最初的共鸣表明我的文章将人们的注意力集中在最佳实践问题而不是技术问题上。但是,我想关注一个技术问题,下面只是一个玩具示例:

如果有人将列表传递给函数参数,您如何捕获和检查该列表中的各个元素,而不会冒系统尝试调用/评估这些元素的风险?

例如,如果用户向参数传递一个可能合适或不合适的函数列表,或者加载了相关的包,该函数如何安全地检查请求的函数?


假设我想构建一个迭代可能应用的其他函数的函数。实际示例会调用不同的建模函数,但这里有一个更容易看到的玩具示例:

newfunc <- function(func.list){
  lapply(func.list, 
         function(f){
           f(letters)
         }
  )
}

假设 newfunc() 可以使用的函数包括函数 nchar() 和 length()。如果我们提供这些,我们会得到以下信息:

newfunc(
  func.list = list(nchar, length)
)


[[1]]
 [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

[[2]]
[1] 26

但是,假设 newfunc() 也能够采用 str_to_upper() 之类的东西,它来自包stringr。传递 str_to_upper() 可以正常工作,但前提是预先加载stringr :

newfunc(
  func.list = list(nchar, length, str_to_upper)
)

Error in lapply(func.list, function(f) f(letters)) : 
  object 'str_to_upper' not found


require(stringr)

newfunc(func.list = list(nchar, length, str_to_upper))
[[1]]
 [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

[[2]]
[1] 26

[[3]]
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O"
[16] "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"


我想将代码放在可以调查列表元素并确定是否需要加载任何包(如stringr )的函数中。另外,我想检查列出的函数是否来自可接受的集合(如果有人通过mean(),或者更糟糕的是,rcorr()来自未加载的Hmisc ,它会捕获)。

# This works here but is undesireable:
newfunc(func.list = list(nchar, length, str_to_upper, mean))

# This creates issues no matter what:
newfunc(func.list = list(nchar, length, str_to_upper, rcorr))
require(Hmisc)
newfunc(func.list = list(nchar, length, str_to_upper, rcorr))

我知道如何func.list.test <- deparse(substitute(func.list)获取参数的文字文本,但我不知道如何在单个元素上执行此操作而不会在某些函数不存在时触发错误。

(而且我不想对 的整体解析输出采取字符串操作的hacky路线func.list.test

理想情况下,对于这个用例,我想知道这是否可以使用基本 R 技术来完成。但是,如果这是最好的/唯一的方法,请随时解释如何使用更新的方法(如整洁的评估/quosures)来做到这一点(尽管知道我目前对这些方法的熟悉程度非常有限)。

任何帮助,将不胜感激。

4

1 回答 1

1

这是一个纯base函数,用于find()确定正在使用的函数并help.search()定位任何可能具有该函数的已安装包:

resolve <- function( func.list )
{
  ## Disassemble the supplied list of functions (lfs)
  lf <- as.list(substitute( func.list ))[-1]
  lfs <- lapply( lf, deparse )
  lfs <- setNames( lfs, lfs )

  ## Find functions (ff) in the loaded namespaces
  ff <- lapply( lfs, find )

  ## Existing functions (fex) are listed in the order of masking
  ##   The first element is used by R in the absence of explicit ::
  fex <- subset( ff, lapply(ff, length) > 0 )
  fex <- lapply( fex, `[`, 1 )

  ## Search for empty entries (ee) among installed packages
  ee <- names(subset( ff, lapply(ff, length) < 1 ))
  ee <- setNames( ee, ee )
  eeh <- lapply( ee, function(e)
      help.search( apropos = paste0("^", e, "$"),
                  fields = "name", ignore.case=FALSE )$matches$Package )

  ## Put everything together
  list( existing = fex, to_load = eeh )
}

示例用法:

resolve(func.list = list(nchar, length, str_to_upper, lag, between))
# List of 2
#  $ existing:List of 3
#   ..$ nchar : chr "package:base"
#   ..$ length: chr "package:base"
#   ..$ lag   : chr "package:stats"
#  $ to_load :List of 2
#   ..$ str_to_upper: chr "stringr"
#   ..$ between     : chr [1:3] "data.table" "dplyr" "rex"

library(dplyr)
resolve(func.list = list(nchar, length, str_to_upper, lag, between))
# List of 2
#  $ existing:List of 4
#   ..$ nchar  : chr "package:base"
#   ..$ length : chr "package:base"
#   ..$ lag    : chr "package:dplyr"
#   ..$ between: chr "package:dplyr"
#  $ to_load :List of 1
#   ..$ str_to_upper: chr "stringr"

library(data.table)
resolve(func.list = list(nchar, length, str_to_upper, lag, between))
# List of 2
#  $ existing:List of 4
#   ..$ nchar  : chr "package:base"
#   ..$ length : chr "package:base"
#   ..$ lag    : chr "package:dplyr"
#   ..$ between: chr "package:data.table"
#  $ to_load :List of 1
#   ..$ str_to_upper: chr "stringr"
于 2020-01-30T17:50:25.330 回答