R - 故意掩盖包函数中的函数?

时间:2013-03-07 02:39:13

标签: r

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

作为一个可重现的(我认为)示例,请查看函数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而不是require

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

2 个答案:

答案 0 :(得分: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))

  }

答案 1 :(得分:0)

我正在尝试上面解释的 mnel mathematical.coffee 相同的overrideIn技巧,最后我找到了来自here的另一种选择:

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包装这些帮助函数。