我正在开发我的第一个R包,对于来说,对于我来说尚不清楚的东西是在Description文件中导入。我浏览了很多说明软件包结构的指南,但是找不到我的问题的答案,所以这是我的情况。

  • 我定义了要导出的函数f,因此其定义将在顶部带有正确的@export roxygen注释。
  • 现在
  • ,我的函数f调用了我不想导出的子例程hidden。函数hidden也使用其他软件包,例如X软件包。

  • 因为对X的调用是在hidden函数内部,所以我的函数@import X中没有标签f。因此,我在我的DESCRIPTION文件中的X中添加了Imports包,希望在那里指定相关的依赖项。

    但是,当我使用devtools::document()时,生成的NAMESPACE不包含X的条目。我可以看到发生这种情况的原因:解析器只是在roxygen注释中找不到f的标志,并且在运行时对f的调用崩溃,因为缺少X

    现在,我可以通过在X的导入中指定f来修复所有问题。但是为什么机制如此棘手?或者,类似地,为什么我在DESCRIPTION中的导入与NAMESPACE中的导入不匹配?

    最佳答案

    我的理解是,有三种“正确”的方式来进行导入。 “正确”是指它们将通过CRAN检查并正常运行。选择哪个选项是要平衡各种优势,并且在很大程度上是主观的。

    我将在下面使用术语回顾这些选项

  • primary_function您要导出的包中的函数
  • hidden primary_function所使用的软件包中未导出的函数
  • thirdpartypkg::blackboxblackbox是从thirdpartypkg包中导出的函数。

  • 选项1(无直接导入/显式函数调用)

    我认为这是最常见的方法。在Description文件中声明了thirdpartypkg,但是在NAMESPACE文件中没有从thirdpartypkg导入任何内容。在此选项中,所必需的,以便使用thirdpartypkg::blackbox构造来获得所需的行为。
    # DESCRIPTION
    
    Imports: thirdpartypkg
    
    # NAMESPACE
    export(primary_function)
    
    
    #' @name primary_function
    #' @export
    
    primary_function <- function(x, y, z){
      # do something here
      hidden(a = y, b = x, z = c)
    }
    
    # Unexported function
    #' @name hidden
    
    hidden <- function(a, b, c){
      # do something here
    
      thirdpartypkg::blackbox(a, c)
    }
    

    选项2(直接导入/无显式函数调用)

    在此选项中,您直接导入blackbox函数。这样做之后,就不再需要使用thirdpartypkg::blackbox了;您可以简单地调用blackbox,就好像它是软件包的一部分一样。 (从技术上讲,您将其导入了 namespace ,因此无需获取另一个 namespace 即可获取它)
    # DESCRIPTION
    
    Imports: thirdpartypkg
    
    # NAMESPACE
    export(primary_function)
    importFrom(thirdpartypkg, blackbox)
    
    
    #' @name primary_function
    #' @export
    
    primary_function <- function(x, y, z){
      # do something here
      hidden(a = y, b = x, z = c)
    }
    
    # Unexported function
    #' @name hidden
    #' @importFrom thirdpartypkg blackbox
    
    hidden <- function(a, b, c){
      # do something here
    
      # I CAN USE blackbox HERE AS IF IT WERE PART OF MY PACKAGE
      blackbox(a, c)
    }
    

    选项3(直接导入/显式函数调用)

    您的最后一个选项组合了前两个选项,并将blackbox导入您的 namespace ,然后使用thirdpartypkg::blackbox构造来利用它。从有效的意义上来说,这是“正确的”。但这可以说是浪费和多余的。

    我说这是浪费和多余的原因是,将blackbox导入到您的 namespace 后,您再也不会使用它了。相反,您在blackbox命名空间中使用了thirdpartypkg。从本质上讲,blackbox现在存在于两个 namespace 中,但是只有其中一个已被使用。这就引出了为什么要完全制作副本的问题。
    # DESCRIPTION
    
    Imports: thirdpartypkg
    
    # NAMESPACE
    export(primary_function)
    importFrom(thirdpartypkg, blackbox)
    
    
    #' @name primary_function
    #' @export
    
    primary_function <- function(x, y, z){
      # do something here
      hidden(a = y, b = x, z = c)
    }
    
    # Unexported function
    #' @name hidden
    #' @importFrom thirdpartypkg blackbox
    
    hidden <- function(a, b, c){
      # do something here
    
      # I CAN USE blackbox HERE AS IF IT WERE PART OF MY PACKAGE
      # EVEN THOUGH I DIDN'T. CONSEQUENTLY, THE blackbox I IMPORTED
      # ISN'T BEING USED.
      thirdpartypkg::blackbox(a, c)
    }
    

    注意事项

    那么哪种才是最好的使用方法呢?确实没有一个简单的答案。我会说备选方案3可能不是采取的方法。我可以告诉你,Wickham建议不要使用选项3(我一直在该框架下进行开发,而他建议我反对该选项)。

    如果我们在选项1和选项2之间进行选择,我们必须考虑的因素是1)编写代码的效率,2)读取代码的效率和3)执行代码的效率。

    在提高代码编写效率方面,通常使用@importFrom thirdpartypkg blackbox更容易,并且避免使用::运算符。它只保存了一些按键。但是,这不利地影响了代码的可读性,因为现在尚不清楚blackbox的来源。

    在读取代码的效率方面,最好省略@importFrom并使用thirdpartypkg::blackbox。这使得blackbox的来源很明显。

    关于执行代码的效率,最好使用@importFrom。调用thirdpartypkg::blackbox比使用@importFrom和调用blackbox慢约0.1毫秒。那不是很多时间,所以可能不是很多考虑。但是,如果您的程序包使用了数百个::构造,然后陷入循环或重采样过程,那么这些毫秒数可能会开始累加。

    最终,我认为我读过的最佳指南(并且我不知道在哪里)是,如果您要多次调用blackbox,那么值得使用@importFrom。如果在一个包中只调用三到四次,请继续使用::构造。

    关于r - R包:当我的导出函数未从其他包中显式调用函数,而子例程执行时,“import”的工作方式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49427767/

    10-13 09:08