编写健壮的 R 代码:命名空间、屏蔽和使用 `::` 运算符

2024-05-14

简洁版本

对于那些不想阅读我的“案例”的人来说,这就是本质:

  1. 最小化新包破坏现有代码(即编写您编写的代码)的机会的推荐方法是什么尽可能坚固?
  2. 充分利用该功能的推荐方法是什么?命名空间机制 when

    a) just using贡献的软件包(比如在一些 R 分析项目中)?

    b) 关于发展自己的包?

  3. 如何最好地避免冲突正式课程(大多参考类 http://stat.ethz.ch/R-manual/R-devel/library/methods/html/refClass.html在我的例子中)因为甚至没有一个命名空间机制可以与::上课(AFAIU)?


R 宇宙的运作方式

这件事已经在我的脑海里萦绕了大约两年,但我觉得我似乎没有找到令人满意的解决方案。而且我感觉情况越来越糟了。

我们看到网上的包裹数量不断增加CRAN http://cran.at.r-project.org/web/packages/available_packages_by_name.html, github https://github.com/, R-Forge http://r-forge.r-project.org/等等,这简直太棒了。

在这样一个去中心化的环境中,组成 R 的代码库(假设是base R and 贡献R为简单起见)将偏离鲁棒性方面的理想状态:人们遵循不同的约定,有 S3、S4、S4 参考类等。事物不可能像存在“对齐”那样“对齐”中央清算实例“这强制执行了约定。没关系。

问题

鉴于上述情况,使用 R 编写健壮的代码可能非常困难。并非您需要的所有内容都会在基础 R 中。对于某些项目,您最终将加载相当多的贡献包。

恕我直言,这方面最大的问题是命名空间概念在 R 中的使用方式:R 允许简单地编写某个函数/方法的名称,而无需明确要求其命名空间(即foo vs. namespace::foo).

因此,为了简单起见,这就是每个人都在做的事情。但这样一来,名称冲突、损坏的代码以及重写/重构代码的需要只是时间问题(或加载的不同包数量的问题)。

最好的情况下,你会know关于哪些现有函数被新添加的包屏蔽/重载。最坏的情况是,在代码损坏之前您将一无所知。

举几个例子:

  • 尝试加载RMySQL http://cran.at.r-project.org/web/packages/RMySQL/index.html and RSQLite http://cran.at.r-project.org/web/packages/RSQLite/index.html与此同时,他们相处得不太好
  • also RMongo http://cran.at.r-project.org/web/packages/RMongo/index.html将覆盖某些功能RMySQL http://cran.at.r-project.org/web/packages/RMySQL/index.html
  • forecast http://cran.at.r-project.org/web/packages/forecast/index.html掩盖了很多与 ARIMA 相关函数相关的内容
  • R.utils http://cran.at.r-project.org/web/packages/R.utils/index.html甚至掩盖base::parse常规

(我不记得具体是哪些功能导致了问题,但如果有兴趣,我愿意再次查找)

令人惊讶的是,这似乎并没有困扰很多程序员。我曾多次尝试提高兴趣r-devel http://tolstoy.newcastle.edu.au/R/e15/devel/11/08/0416.html,没有太大作用。

使用的缺点::操作员

  1. 使用::在某些情况下,操作员可能会严重损害效率,如多米尼克·桑佩里 (Dominick Samperi)指出 http://tolstoy.newcastle.edu.au/R/e15/devel/11/10/0716.html.
  2. When 发展你自己的包,你甚至不能使用::运算符贯穿您自己的代码,因为您的代码还不是真正的包,因此还没有命名空间。所以我最初必须坚持foo方式,构建,测试,然后返回将所有内容更改为namespace::foo。并不真地。

避免这些问题的可能解决方案

  1. Reassign每个包中的每个函数到一个遵循特定命名约定的变量,例如namespace..foo为了避免与相关的低效率namespace::foo(我概述过一次here http://tolstoy.newcastle.edu.au/R/e15/devel/11/08/0416.html)。优点:它有效。缺点:它很笨拙,而且使用的内存会增加一倍。
  2. Simulate开发包时的命名空间。 AFAIU,这真的不可能,至少我是那时就这么说过 http://tolstoy.newcastle.edu.au/R/e13/devel/11/01/0039.html.
  3. Make it 强制的 to use namespace::foo。恕我直言,那将是最好的选择。当然,我们会失去一定程度的简单性,但话又说回来,R 宇宙不再简单了(至少不像 00 年代初那么简单)。

那么(正式)课程呢?

除了上述几个方面之外,::方式对于函数/方法来说效果很好。但是类定义又如何呢?

取包裹timeDate http://cran.at.r-project.org/web/packages/timeDate/index.html与它的班级timeDate。假设另一个包也有一个类timeDate。我不明白如何明确声明我想要一个新的类实例timeDate来自两个包中的任何一个。

像这样的事情是行不通的:

new(timeDate::timeDate)
new("timeDate::timeDate")
new("timeDate", ns="timeDate")

随着越来越多的人转向 OOP 风格的 R 包,这可能是一个大问题,导致大量的类定义。如果有is一种显式寻址类定义的名称空间的方法,我非常感谢指针!

结论

尽管这有点长,但我希望我能够指出核心问题,并且我可以在这里提高更多的认识。

I think devtools http://cran.at.r-project.org/web/packages/devtools/index.html and mvbutils http://cran.at.r-project.org/web/packages/mvbutils/index.html确实有一些可能值得传播的方法,但我确信还有更多要说的。


好问题。

验证

编写健壮、稳定且可用于生产的 R 代码非常困难。你说:“令人惊讶的是,这似乎并没有困扰很多程序员”。那是因为大多数 R 程序员不写生产代码。他们正在执行一次性的学术/研究任务。我会严重质疑任何声称 R 很容易投入生产的编码人员的技能。除了您已经链接到的关于搜索/查找机制的帖子之外,我还写了一篇关于危险的帖子warning http://obeautifulcode.com/R/A-Warning-About-Warning/。这些建议将有助于降低生产代码的复杂性。

编写健壮/生产 R 代码的技巧

  1. 避免使用 Depends 的包并支持使用 Imports 的包。仅将依赖项填充到 Imports 中的包使用起来是完全安全的。如果您绝对必须使用采用 Depends 的软件包,请在致电后立即向作者发送电子邮件install.packages().

以下是我告诉作者的内容:“作者您好,我是 XYZ 软件包的粉丝。我想提出一个请求。您能否在下次更新中将 ABC 和 DEF 从 Depends 移至 Imports?我无法将您的软件包添加到我自己的包的导入,直到这种情况发生。随着 R 2.14 为每个包强制命名空间,来自 R Core 的一般信息是包应该尝试成为“好公民”。如果我必须加载 Depends 包,它会增加一个重大的负担:每次依赖新包时,我都必须检查冲突。使用导入,该包没有副作用。我知道这样做可能会破坏其他人的包。我认为这是正确的做法展示对 Imports 的承诺,从长远来看,它将帮助人们生成更强大的 R 代码。”

  1. 使用导入。不要将整个包添加到 Imports,仅添加您需要的特定功能。我通过 Roxygen2 函数文档和自动生成 NAMESPACE 文件的 roxygenize() 来完成此操作。通过这种方式,您可以导入两个存在冲突的包,而冲突并不在您实际需要使用的函数中。这很乏味吗?直到它成为一种习惯。好处:您可以快速识别所有第 3 方依赖项。这有助于...

  2. 不要盲目升级软件包。逐行阅读变更日志并考虑更新将如何影响您自己的软件包的稳定性。大多数时候,更新不会触及您实际使用的功能。

  3. 避免中四课程。我在这里挥手。我发现S4很复杂,需要足够的脑力来处理R功能方面的搜索/查找机制。你真的需要这些OO功能吗?管理状态 = 管理复杂性 - 留给 Python 或 Java =)

  4. 编写单元测试。使用 testthat 包。

  5. 每当您 R CMD 构建/测试您的包时,请解析输出并查找“注释”、“信息”、“警告”。另外,用自己的眼睛进行物理扫描。构建步骤的一部分会记录冲突,但不会附加警告等。

  6. 在调用第 3 方包后立即添加断言和不变量。换句话说,不要完全相信别人给你的东西。稍微探究一下结果stop()如果结果出乎意料。您不必发疯 - 选择一两个暗示有效/高可信度结果的断言。

我认为还有更多,但现在这已经成为肌肉记忆 =) 如果我有更多,我会增强。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写健壮的 R 代码:命名空间、屏蔽和使用 `::` 运算符 的相关文章

随机推荐

  • Rails 4 - 如何链接到 PDF 文件(名称.PDF)?

    我正在生成 PDF 文件 我的链接如下所示 当我点击这个时 它会带我去 display invoice 123456789 这是一个 HTML 版本 在控制器中的操作如下 def display invoice if params invo
  • 文本视图不显示全文

    我正在使用 TableLayout 和 TableRow 创建一个简单的布局 其中包含两个 TextView 这是代码的一部分
  • 如何更改元素的 CSS 类并在单击时删除所有其他类

    我如何处理 AngularJS 2 中的一种情况 即单击一个元素需要更改其自己的样式 并且如果其他元素具有该样式 则需要将其删除 最好在一个函数中 如同Angular js 如何在单击时更改元素 css 类并删除所有其他元素 https s
  • 如何在html中设置按钮的文本大小

    您好 我想在我的网站上有一个按钮 并且我想调整按钮上的文本大小 我该怎么做呢 我的代码如下
  • 增加内存限制时出现奇怪的错误

    我使用的是共享托管环境 PHP 的默认内存限制是 32M 我在 Concrete5 设置方面遇到一些问题 当我尝试登录 Concrete5 的管理面板时 出现内存限制错误Allowed memory size of 33554432 byt
  • 将带有 glut 的点击坐标添加到向量链接列表中

    我想创建一个向量链接列表 并在 GLUT 库的帮助下获取点击的位置并将它们附加到链接列表中 这些是我写的结构 typedef struct vector int x int y Vector typedef struct VectorLis
  • 在 Spring Security ACL 中授予权限

    我在 grails 1 3 7 中使用 Spring Security ACL 插件 但我的问题可能比这更通用 我想允许拥有以下权限的用户BasePermission READ访问对象以便能够向其他用户授予相同的权限 如果 user1 具有
  • 如何在 C++ 中将 CString 转换为 double?

    我如何转换CString to a double在 C 中 Unicode 支持也很好 Thanks A CString可以转换为LPCTSTR 这基本上是一个const char const wchar t 在 Unicode 版本中 知
  • Rails 中的 PDF 导出

    我需要将包含一些图表的 HTML 页面导出为 PDF 有哪些好的 gem 可以做到这一点 PDFKit http railscasts com episodes 220 pdfkit http railscasts com episodes
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • 更改用作函数全局作用域的字典

    我想做一个 purePython 的装饰器 其中一部分是能够有选择地禁止访问函数的全局范围 有没有一种方法可以以编程方式更改哪个字典事物充当函数的全局 外部作用域 因此 例如在下面我希望能够拦截对f in h并抛出错误 但我想允许访问g因为
  • 异步异常处理程序:在事件循环线程停止之前不会被调用

    我正在我的异步事件循环上设置异常处理程序 但是 在事件循环线程停止之前 它似乎不会被调用 例如 考虑以下代码 def exception handler loop context print Exception handler called
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • 在Racket中将结构递归转化为累积递归

    我有一些代码来查找最大高度并将其替换为关联的名称 身高和姓名有单独的列表 每个列表的长度相同且非空 我可以使用结构递归来解决这个问题 但必须将其更改为累积递归 而且我不确定如何做到这一点 我见过的所有例子都让我困惑 有人能够将代码变成使用累
  • 具有挑战性的问题 - 使用 PHP 对 XML 数据进行排序

    我有 xml 文件 其中包含大量产品数据 我需要根据我的字段 ProductRange 的数据对我的产品进行排序 ProductRange urldecode GET Range XML 文件数据
  • 如何从 postgresql 函数或触发器发送一些 http 请求

    我需要通过 http 协议 GET 或 POST 请求 从函数或触发器发送数据 是否可以 您可以尝试用 PL Python 编写触发器并使用 urllib2 进行 POST
  • RuntimeError(f"目录 '{directory}' 不存在") RuntimeError: 目录 'app/static' 不存在

    当我运行 server py 文件时出现错误 File C Users nawin AppData Local Programs Python Python38 lib site packages starlette staticfiles
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • 使用 FastAPI 传输 LangChain OpenAI 响应 [重复]

    这个问题在这里已经有答案了 我想将 OpenAI 的响应直接传输到 FastAPI 的端点 Code 在我的threads handler py 位于单独的文件夹中 中 我有以下函数askQuestion def askQuestion s
  • 编写健壮的 R 代码:命名空间、屏蔽和使用 `::` 运算符

    简洁版本 对于那些不想阅读我的 案例 的人来说 这就是本质 最小化新包破坏现有代码 即编写您编写的代码 的机会的推荐方法是什么尽可能坚固 充分利用该功能的推荐方法是什么 命名空间机制 when a just using贡献的软件包 比如在一