【问题标题】:Grouping functions from an R package对 R 包中的函数进行分组
【发布时间】:2020-10-17 20:57:38
【问题描述】:

上下文

我正在开发一个 R 包,其中包含超过 100 页的函数,这些函数分为 10 个不同的主题。

期望的行为

我希望用户在调用函数时能够考虑到这些主题。

例如,用户不必调用foo(),而是必须这样做

load(theme1) # or whatever would be used to load a subgroup of functions
foo()

或类似的东西

theme1$foo()

我想阻止用户做的是直接加载包并调用函数而不考虑主题,即library(package); foo()

我尝试过的

我已经使用modules packageprevious package 中完成后一种解决方案,但是由于我的函数是相互依赖的,这一次该软件包的已发布版本不能很好地工作(我已经打开了一个GitHub issue 讨论主题)。

我还考虑编写自己的解决方案,也许是一些简单的事情,涉及创建一个 sublibrary() 函数,一次只导出几个函数,但我没有得到任何结果。

当然,我想发布 10 个不同的包在技术上也是可行的,但我认为这种解决方案太笨拙了。

问题

除了使用模块,有没有办法实现以上功能?

【问题讨论】:

    标签: r module package grouping r-package


    【解决方案1】:

    这里可以使用面向对象的编程,其中theme1 是一个类或从该类实例化的对象,与之关联的函数是方法。这里有许多替代方案,包括 R 参考类(通过?ReferenceClasses 获得更多信息)、R6 packageproto packageR.oo package 和函数范围(通过demo("scoping") 获得更多信息。

    例如使用 proto 包:

    library(proto)
    
    theme1 <- proto(
      foo = function(.) print("foo"),
      bar = function(.) print("bar")
    )
    
    theme1$foo()
    ## [1] "foo"
    theme1$bar()
    ## [1] "bar"
    

    如果只是分组问题,如果您不需要本地存储或继承,那么我们甚至可以使用列表:

    theme1 <- list(foo = function() print("foo"),
                   bar = function() print("bar"))
    
    theme1$foo()
    ## [1] "foo"
    theme1$bar()
    ## [1] "bar"
    

    可以使用许多其他 OO 方法,但它们涉及创建一个类,然后用于创建单个对象(这是一个额外的步骤);但是,它们确实支持问题的 $ 语法。

    使用 R6 我们有:

    library(R6)
    
    Theme1 = R6Class("theme1",
      public = list(
        foo = function() print("foo"),
        bar = function() print("bar")
      )
    )
    
    theme1 <- Theme1$new() # generate a Theme1 object
    
    theme1$foo()
    ## [1] "foo"
    theme1$bar()
    ## [1] "bar"
    

    或使用参考类(不需要包):

    setRefClass(
      "Theme1",
      methods = list(
        foo = function() print("foo"),
        bar = function() print("bar")
      )
    )
    
    theme1 <- Theme1()  # generate Theme1 object
    
    theme1$foo()
    ## [1] "foo"
    theme1$bar()
    ## [1] "bar"
    

    或使用函数作用域(不需要包):

    Theme1 <- function() list(
      foo = function() print("foo"),
      bar = function() print("bar")
    )
    
    theme1 <- Theme1()
    
    theme1$foo()
    ## [1] "foo"
    theme1$bar()
    ## [1] "bar"
    

    【讨论】:

    • 感谢您抽出宝贵时间做出如此完整的回答!我还在习惯S4,简直不敢相信他们已经到了6! xD
    【解决方案2】:

    这里有一个关于如何实现sublibrary 功能的想法。在这种情况下,我们将使用它来加载ggplot2 的一小部分

    sublibrary <- function(theme) {
      subgroups <- 
        list(basic   = list(ggplot = ggplot2::ggplot,
                            aes = ggplot2::aes,
                            geom_line = ggplot2::geom_line,
                            geom_point = ggplot2::geom_point),
            advanced = list(scale_color_manual = ggplot2::scale_color_manual,
                            theme = ggplot2::theme))
      
      attach(list2env(subgroups[[theme]]))
    }
    

    这意味着我可以不加载整个包:

    sublibrary("basic")
    
    ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
      geom_point()
    

    实际上,subgroup 对象将存储在函数外部、包命名空间内的列表或环境中。

    【讨论】:

    • 这似乎可以解决问题,非常感谢如此!我曾与attach 一起解决这个问题,但我不知道list2env,所以我走不远。我对它处理函数文档的能力感到兴奋(即,我必须将函数设置为内部函数,以便它们在加载之前保持隐藏状态,但加载后我仍然可以使用 ? 打开他们的文档)。
    • 我认为attach 不会通过 CRAN 检查。请参阅?attach 中的良好实践部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多