4

我正在尝试屏蔽包中的函数调用的函数。

作为可重现(我认为)的示例,请查看函数isTRUE

function (x) 
identical(TRUE, x)

假设出于某种原因我想identical总是返回"foobar"因此isTRUE总是返回“foobar”:

# try override identical
identical <- function(...) { return('foobar') }
identical(TRUE, 'a') # 'foobar', as expected

现在我调用isTRUE,希望该identical函数中的调用将访问我的屏蔽版本,但它没有:

isTRUE('a') # hope that this will return 'foobar'
# [1] FALSE

所以一般来说,我如何临时导致从打包函数中调用的函数返回不同的东西?

语境

我的包中有一个功能:

myFunc <- function(...) {
    if (!require(rgdal)) {
        stop("You do not have rgdal installed")
    }
    # ...
}

我想测试一下,如果没有安装 rgdal,函数会抛出错误。但是,我确实安装了 rgdal。我想myFunc认为它不是(暂时),所以我正在尝试做:

require <- function(...) { return(FALSE) }

在打电话之前myFunc希望它会失败。但是,它似乎myFunc并没有被这个欺骗,并且仍然调用base::require而不是 my require

(是的,这似乎是一件微不足道的事情,因为myFunc如果我没有安装 rgdal 肯定会抛出错误,但假设现在情况更复杂,我想以同样的方式进行测试 - 我的问题仍然存在)

4

2 回答 2

2

您可以以编程方式创建一个函数

foo <- function(...) if(!require('MASS')) stop('foo')

testfun <- function(fun){
  require <- function(...) FALSE
  fff <- function(){}
    formals(fff) <- formals(fun)
    body(fff) <- body(fun)
  fff

}

testfoo <- testfun('foo')

require在创建函数时按原样定义testfun

foo()
## Loading required package: MASS

detach(package:MASS)

testfoo()
# Error in testfoo() : foo

你可以用 做类似的事情local,但我认为它会更混乱

例如

testfoo2 <- local({
  require <- function(...) FALSE
  foo <- function(...) if(!require('MASS')) stop('foo')
  })

testfoo2()
## Error in testfoo2() : foo

(来自 mathcoffee - 基于此答案的后续行动)。

我能够定义一个函数:

overrideIn <- function(f, ...) {                                                
    overrides <- list(...)                                                      
    nms <- names(overrides)[names(overrides) != '']                             
    # stub out the functions                                                    
    for (nm in nms) {                                                           
        assign(nm, overrides[[nm]])                                             
    }                                                                           

    # copy over f                                                               
    fff <- function () {}                                                       
    formals(fff) <- formals(f)                                                  
    body(fff) <- body(f)                                                        
    return(fff)                                                                 
}

这样我就可以做到

f <- overrideIn(myFunc, require=function (...) FALSE)

现在,当我调用f它时,它具有被覆盖的版本require,所以我可以这样做(使用奇妙的testthat包):

expect_that(f(), throws_error('You do not have rgdal installed'))

一个稍微简洁的覆盖函数版本(再次是 mnel)

overRideIn2 <- function(fun,...){
   e <- environment()
   .list <- list(...)
   ns <- nchar(names(.list))>0L

   list2env(.list[ns], envir = e) 

   fff <- as.function(as.list(fun))

  }
于 2013-03-07T03:04:29.047 回答
0

我正在尝试mnelmath.coffee上面解释的相同overrideIn技巧,我最终从这里找到了另一种选择:

overRideIn3 <- function(fun,...){
    e <- environment()
    overrides <- list(...)
    ns <- nchar(names(overrides)) > 0L

    list2env(overrides[ns], envir = e) 

    environment(fun) <- e
    fun
}

请注意,所有这些动态作用域技巧仅适用于fun. 这意味着任何fun调用的辅助函数都无法访问提供的定义,overrides除非fun也用overRideIn.

于 2017-07-18T15:11:30.610 回答