【问题标题】:R language Function Factory: How to ensure safety?R语言函数工厂:如何保证安全?
【发布时间】:2020-11-22 02:24:11
【问题描述】:

问题

我想检查 R 中的函数工厂是否“安全”。这里的“安全”意味着工厂创建的函数的结果仅取决于它们的参数,而不是全局变量。

说明

这是一个不安全的工厂:

funfac_bad = function(){  
  newfun = function()
    return(foo)
  return(newfun)
}

newfun 的返回值将取决于执行 newfun 时 foo 的值。如果 foo 恰好是未定义的,它甚至可能会发生错误。

现在 - 很明显 - 这个工厂可以通过将 foo 绑定到工厂内部的值来保证安全

funfac_good = function(){
  foo = 4711
  newfun = function()
    return(foo)
  return(newfun)
}

我认为我可以通过检查工厂中的全局变量来验证安全性。确实:

> codetools::findGlobals(funfac_bad) 
[1] "{"      "="      "foo"    "return"
> codetools::findGlobals(funfac_good)
[1] "{"      "="      "return"

但我的实际用例(非常)复杂。工厂的功能依赖于数百行代码的子函数和变量。因此,我获取了定义,我的工厂原则上如下所示:

funfac_my = function(){
  sys.source("file_foo.R", envir = environment())
  newfun = function()
    return(foo)
  return(newfun)
}

当且仅当在“file_foo.R”中执行的代码将名称“foo”绑定到一个值时,这是一个安全工厂。 但是(非常合乎逻辑)codetools::findGlobals 将始终将“foo”报告为全局变量。

问题

当定义来源时,如何检测此类函数工厂的不安全行为?

【问题讨论】:

  • 显然你不能,因为"file_foo.R" 可能会在你检查的时间和你调用函数的时间之间发生变化。它不可能是安全的。
  • 文件的更改不是问题,因为我可以检查并立即创建函数(或写保护文件或其他)。一旦创建,这些函数就会是安全的,因为它们独立于工厂。
  • 为什么不在主体中使用file_foo.R 代码构建一个函数,然后检查一下?这仅意味着更改文件中的大约 5 行,以包含 sys.source() 调用之前和之后的内容。如果你知道file_foo.R 不会改变,为什么不从一开始就把它放在函数中呢?
  • 是的,但在我的情况下,源文件包含嵌套函数/变量定义和数百行代码。简单地将所有这些倾倒到工厂中可能会让人难以理解。

标签: r function functional-programming namespaces


【解决方案1】:

为什么不确保在获取外部文件之前在本地为 foo 定义一个默认值?例如,假设我有这个文件:

foo.R

foo <- "file foo"

还有这个文件

bar.R

bar <- "bar"

如果我这样写我的函数工厂:

funfac_my <- function(my_path) {
  foo <- "fun fac foo"
  if(!missing(my_path)) sys.source(my_path, envir = environment())
  function() foo
}

然后我得到以下结果:

foo <- "global foo"

funfac_my("foo.R")()
#> [1] "file foo"

funfac_my("bar.R")()
#> [1] "fun fac foo"

funfac_my()()
#> [1] "fun fac foo"

因此输出永远不会依赖于全局环境中是否存在名为“foo”的对象,(除非您恶意运行的脚本会寻找一个名为“foo”的全局对象进行复制 - 但那可能是无论如何,通过采购该文件来获得您想要的东西)

请注意,您可以通过在最后一行之前添加行 if(foo == "fun fac foo") stop("object 'foo' not found") 将其设置为抛出错误而不是返回默认值。因此,即使您在全局工作区中有一个名为 foo 的错误对象,这也会抱怨找不到 foo

【讨论】:

  • 是的,这可能有效。但是定义默认值在某种程度上超出了在源文件中“隐藏”定义的目的。在某种程度上,人们可能会争论为什么首先要有“来源”?错误的一个原因是忘记/错误输入名称。无法通过检查默认值来减轻这种风险。
【解决方案2】:

您问“当定义来源时,我如何检测这种函数工厂的不安全行为?”我认为答案是你不能,但稍微改变一下就会很容易。

例如,假设你现在有

foo <- undefined_value

作为"file_foo.R" 中的唯一行,并且您希望收到有关使用undefined_value 的警告。我的建议是你不要那样做。相反,将funfac_my 的整个定义放入"file_foo.R",将那一行换行:

funfac_my = function(){
 
  foo <- undefined_value

  newfun = function()
    return(foo)
  return(newfun)
}

现在您可以获取该文件,并将函数 funfac_my 传递给 codetools::findGlobals

codetools::findGlobals(funfac_my)
#> [1] "{"               "<-"              "="               "return"         
#> [5] "undefined_value"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-14
    • 2019-11-24
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 2011-10-11
    相关资源
    最近更新 更多