gpt4 book ai didi

r - 编写强大的 R 代码 : namespaces, 屏蔽并使用 `::` 运算符

转载 作者:行者123 更新时间:2023-12-04 22:42:34 32 4
gpt4 key购买 nike

精简版

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

  • 最小化新包破坏现有代码的机会的推荐方法是什么,即制作您编写的代码尽可能强大 ?
  • 什么是充分利用 的推荐方式?命名空间机制 什么时候

    a) 只使用贡献的包(比如在一些 R 分析项目中)?

    b) 关于开发自己的软件包?
  • 如何最好地避免与 相关的冲突正规类 (主要是 Reference Classes 在我的情况下)因为甚至没有与 :: 相媲美的命名空间机制类(class)(AFAIU)?


  • R 宇宙的工作方式

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

    我们在 CRAN 上看到越来越多的包裹, github , R-Forge等等,这简直太棒了。

    在这样一个去中心化的环境中,很自然地,组成 R 的代码库(为了简单起见,假设是基础 R 和贡献的 R)会在健壮性方面偏离理想状态:人们遵循不同的约定,有 S3、S4 、S4 引用类等。事情不能像如果有一个强制执行约定的“中央清算实例”那样“对齐”。没关系。

    问题

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

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

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

    充其量,你会 知道 关于哪些现有功能被新添加的包屏蔽/重载。最坏的情况是,在您的代码中断之前,您将一无所知。

    几个例子:
  • 尝试加载 RMySQLRSQLite同时,他们相处得不是很好
  • 还有RMongo将覆盖 RMySQL 的某些功能
  • forecast屏蔽了很多关于 ARIMA 相关函数的内容
  • R.utils甚至掩盖了base::parse例行程序

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

    令人惊讶的是,这似乎并没有打扰很多程序员。我试图在 r-devel 上几次提高兴趣, 无显着效果。

    使用 :: 的缺点运算符(operator)
  • 使用 ::在某些情况下,运算符可能会显着降低效率,如 Dominick Samperi pointed out .
  • 开发中 你自己的包,你甚至不能使用 ::运算符贯穿您自己的代码,因为您的代码还不是真正的包,因此还没有命名空间。所以我最初必须坚持 foo方式,构建,测试,然后返回将所有内容更改为 namespace::foo .并不真地。

  • 避免这些问题的可能解决方案
  • 重新分配 每个包中的每个函数到遵循某些命名约定的变量,例如namespace..foo为了避免与 namespace::foo 相关的低效率(我曾经概述过 here )。优点:它有效。缺点:它很笨拙,而且你使用的内存翻了一番。
  • 模拟 开发包时的命名空间。 AFAIU,这不太可能,至少我是told so back then .
  • 制作 强制性 使用 namespace::foo .恕我直言,这将是最好的做法。当然,我们会失去一些简单性,但是 R 宇宙又不再简单了(至少它不像 00 年代初期那样简单)。

  • 那么(正式)类(class)呢?

    除了上述方面, ::方法对于函数/方法非常有效。但是类定义呢?

    拿包 timeDate与它的类 timeDate .假设另一个包也有一个类 timeDate .我不知道如何明确声明我想要类 timeDate 的新实例从两个包中的任何一个。

    这样的事情是行不通的:
    new(timeDate::timeDate)
    new("timeDate::timeDate")
    new("timeDate", ns="timeDate")

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

    结论

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

    我想 devtoolsmvbutils确实有一些可能值得传播的方法,但我相信还有更多要说的。

    最佳答案

    很好的问题。

    验证

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

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

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

  • 这是我告诉作者的话:“您好作者,我是 XYZ 包的粉丝。我想提出一个请求。您能否在下一次更新中将 ABC 和 DEF 从 Depends 移到 Imports?我无法将您的包添加到我自己的包的导入,直到发生这种情况。随着 R 2.14 为每个包强制执行 NAMESPACE,来自 R Core 的一般信息是包应该尝试成为“好公民”。如果我必须加载依赖包,它会增加一个很大的负担:每次我依赖一个新包时,我都必须检查冲突。使用导入,包没有副作用。我知道你这样做可能会破坏其他人的包。我认为这是正确的做法展示对 Imports 的 promise ,从长远来看,它将帮助人们生成更强大的 R 代码。”
  • 使用 importFrom。不要将整个包添加到 Imports,只添加您需要的那些特定功能。我使用 Roxygen2 函数文档和自动生成 NAMESPACE 文件的 roxygenize() 来完成此操作。通过这种方式,您可以导入两个存在冲突的包,其中冲突不在您实际需要使用的函数中。这很乏味吗?直到它成为一种习惯。好处:您可以快速识别所有 3rd 方依赖项。这有助于...
  • 不要盲目升级软件包。逐行阅读更改日志并考虑更新将如何影响您自己的包的稳定性。大多数情况下,更新不会触及您实际使用的功能。
  • 避免 S4 类(class)。我在这里挥手。我发现 S4 很复杂,需要足够的脑力来处理 R 功能方面的搜索/查找机制。你真的需要这些 OO 功能吗?管理状态 = 管理复杂性 - 将其留给 Python 或 Java =)
  • 编写单元测试。使用 testthat 包。
  • 每当您 R CMD 构建/测试您的包时,解析输出并查找注意、信息、警告。另外,用自己的眼睛进行 body 扫描。构建步骤的一部分记录了冲突,但没有附加警告等。
  • 在调用第 3 方包后立即添加断言和不变量。换句话说,不要完全相信别人给你的东西。稍微探查一下结果和stop()如果结果出乎意料。您不必发疯 - 选择一两个暗示有效/高置信度结果的断言。

  • 我认为还有更多,但现在这已成为肌肉内存 =) 如果更多,我会增加。

    关于r - 编写强大的 R 代码 : namespaces, 屏蔽并使用 `::` 运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10947159/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com