2

假设我有一个具有以下功能的包:

foo <- function() {
  Sys.sleep(1) # really expensive operation
  return(1)
}

每次运行的函数值总是相同的,所以我想使用记忆。

我以为我可以简单地做

foo <- memoise::memoise(function() {
  Sys.sleep(1) # really expensive operation
  return(1)
})

但是,这不起作用。

我的意思是,将它作为 GlobalEnv 函数运行,它可以工作:

foo <- memoise::memoise(function() {
  Sys.sleep(1)
  return(1)
})

system.time(foo())
#>    user  system elapsed 
#>       0       0       1

system.time(foo())
#>    user  system elapsed 
#>    0.01    0.00    0.01

reprex 包(v0.3.0)于 2019 年 12 月 23 日创建

但是,如果它在一个包中,我会得到非常奇怪的行为。基本上,记忆并没有发挥作用,我一直得到相同的成本。但是,如果我打印函数定义,它就会开始工作!

system.time(bar::foo())
#>    user  system elapsed 
#>    0.47    0.08    2.55

system.time(bar::foo())
#>    user  system elapsed 
#>       0       0       2

system.time(bar::foo())
#>    user  system elapsed 
#>    0.02    0.00    2.02

system.time(bar::foo())
#>    user  system elapsed 
#>    0.01    0.00    2.02

bar::foo
#> Memoised Function:
#> function() {
#>   Sys.sleep(2)
#>   return (1)
#> }
#> <environment: namespace:bar>

system.time(bar::foo())
#>    user  system elapsed 
#>       0       0       2

system.time(bar::foo())
#>    user  system elapsed 
#>       0       0       0

system.time(bar::foo())
#>    user  system elapsed 
#>       0       0       0

system.time(bar::foo())
#>    user  system elapsed 
#>       0       0       0

NAMESPACE作为记录,以下是和DESCRIPTION文件的相关部分:

# NAMESPACE
export(foo)
importFrom(memoise,memoise)

# DESCRIPTION [...]
Imports:
    memoise

这里发生了什么,我应该怎么做才能让我的包中的记忆从一开始就起作用?

4

1 回答 1

1

这看起来像memoise包中的错误。当您在自己的包上工作时,R 可能会srcref向函数添加调试信息(称为 s)。每次调用函数时,这些都会导致哈希值不同,因此它永远不会识别出您正在使用相同的参数进行调用。

一个简单的解决方法是在安装自己的包时删除安装选项“--with-keep.source”。(如果您使用的是 RStudio,它会自动添加到 中Project Options | Build Tools | Install and Restart...。)这将阻止 R 添加srcref,并且memoise不会触发中的错误。不幸的是,这会削弱 RStudio 和其他前端中的调试器,因此并不理想。

另一种不会与调试器混淆的解决方法(除了那个函数)是removeSource在被记忆的目标上使用。例如,

foo <- memoise::memoise(removeSource(function() {
  Sys.sleep(1) # really expensive operation
  return(1)
}))
于 2019-12-23T21:05:58.663 回答