对于那些不想阅读我的"案例"的人来说,这就是本质:
什么是在
时充分利用命名空间机制的推荐方法a)只是使用贡献的软件包(比如在一些R分析项目中)?
b)关于开发自己的包裹?
如何避免与正式类(在我的情况下大多数是Reference Classes)发生冲突,因为甚至没有与{{1相似的命名空间机制对于类(AFAIU)?
这种情况在我脑海中唠叨了大约两年了,但我并不觉得我已经找到了令人满意的解决方案。而且我觉得情况越来越糟。
我们在CRAN,github,R-Forge等广告中看到了越来越多的软件包,这简直太棒了。
在这样一个分散的环境中,组成R的代码库很自然(让我们说基础R 和贡献R ,为简单起见,将在健壮性方面偏离理想状态:人们遵循不同的惯例,那里有S3,S4,S4参考类等。事情不能像#34;对齐&# 34;因为如果有一个" 中央清算实例"强制执行的惯例。没关系。
鉴于上述情况,使用R编写健壮的代码可能非常困难。并非您需要的所有内容都在基础R中。对于某些项目,您最终会加载相当多的贡献包。
恕我直言,在这方面最大的问题是命名空间概念在R中使用的方式:R允许简单地写出某个函数/方法的名称而不明确要求它的命名空间(即::
与foo
)。
因此,为了简单起见,这是每个人都在做的事情。但是这样,名称冲突,破坏代码以及重写/重构代码的需要只是时间问题(或者加载的不同包的数量)。
充其量,您将知道有关新添加的软件包屏蔽/重载哪些现有功能。在最坏的情况下,在您的代码中断之前,您将不知道。
几个例子:
namespace::foo
例程(我无法回想起哪些功能特别导致了这些问题,但如果有兴趣,我愿意再次查询)
令人惊讶的是,这似乎并没有给很多程序员带来麻烦。我试图在r-devel几次提高兴趣,但没有显着效果。
base::parse
运算符::
运算符可能会在某些情况下严重影响效率,如Dominick Samperi pointed out。::
运算符,因为您的代码还没有真正的软件包,因此还有还没有命名空间。因此,我必须首先坚持::
方式,构建,测试,然后返回将所有内容更改为foo
。不是真的。namespace::foo
以避免与namespace..foo
相关的效率低下(我概述了here)。优点:它的工作原理。缺点:它笨拙,你使用的内存加倍。namespace::foo
。恕我直言,这将是最好的事情。当然,我们会失去一些简单性,但R宇宙再也不再那么简单了(至少它不像00年代早期那么简单)。除了上述方面之外,namespace::foo
方式对于函数/方法也很有效。但是类定义呢?
将程序包timeDate与其::
类一起使用。说另一个包也有一个类timeDate
。我不知道如何从两个包中的任何一个明确声明我想要一个新的类timeDate
实例。
这样的东西不起作用:
timeDate
这可能是一个巨大的问题,因为越来越多的人为他们的R包切换到OOP风格,导致许多类定义。如果是一种显式地解决类定义的命名空间的方法,我非常感谢指针!
虽然这有点冗长,但我希望我能够指出核心问题/问题,并且我可以在这里提高认识。
答案 0 :(得分:31)
很棒的问题。
编写健壮,稳定且适合生产的R代码很难。你说:“令人惊讶的是,这似乎并没有打扰很多程序员。”那是因为大多数R程序员都没有编写生产代码。他们正在进行一次性的学术/研究任务。我会严重质疑任何声称R很容易投入生产的编码员的技能。除了我已经链接到的关于搜索/查找机制的帖子,我还写了一篇关于warning的危险的帖子。这些建议有助于降低生产代码的复杂性。
install.packages()
后立即通过电子邮件发送给作者。 以下是我告诉作者的内容:“嗨,作者,我是XYZ包的粉丝。我想提出请求。你能否在下次更新时将ABC和DEF从Depends转移到Imports?我无法添加你的软件包到我自己的软件包的Imports,直到这种情况发生.R 2.14为每个软件包强制执行NAMESPACE,来自R Core的一般信息是软件包应该尝试成为“好公民”。如果我必须加载一个Depends软件包,它会添加一个重要的负担:每次我依赖一个新包装时都要检查是否存在冲突。使用Imports,包装没有副作用。我知道你可能会破坏其他人的包装。我认为它是正确的为证明对Imports的承诺而做的事情,从长远来看,它将帮助人们生成更强大的R代码。“
使用importFrom。不要将整个包添加到Imports,只添加您需要的那些特定功能。我用Roxygen2函数文档和roxygenize()自动生成NAMESPACE文件。通过这种方式,您可以导入两个冲突不存在于实际需要使用的函数中的包。这很乏味吗?直到它成为一种习惯。好处:您可以快速识别所有第三方依赖项。这有助于......
不要盲目升级软件包。逐行阅读更改日志,并考虑更新将如何影响您自己的软件包的稳定性。大多数情况下,更新不会触及您实际使用的功能。
避免使用S4课程。我在这里挥手。我发现S4很复杂,它需要足够的脑力来处理R功能方面的搜索/查找机制。你真的需要这些OO功能吗?管理状态=管理复杂性 - 留给Python或Java =)
编写单元测试。使用testthat包。
无论何时R CMD构建/测试您的包,解析输出并查找NOTE,INFO,WARNING。另外,用自己的眼睛进行物理扫描。构建步骤的一部分记录了冲突,但没有附加WARN等。
在调用第三方软件包后立即添加断言和不变量。换句话说,不要完全相信别人给你的东西。如果结果是意外的,请稍微探查一下结果stop()
。你不必发疯 - 选择一两个暗示有效/高可信度结果的断言。
我认为还有更多,但现在已成为肌肉记忆=)如果有更多的话,我会增加。
答案 1 :(得分:15)
我接受它:
总结:灵活性需要付出代价。我愿意支付这个价格。
1)我根本不使用会导致这类问题的软件包。如果我真的需要在我自己的包中使用该包中的函数,我在importFrom()
文件中使用NAMESPACE
。无论如何,如果我遇到包装问题,我会联系包裹作者。问题出在他们身边,而不是R's。
2)我从不在自己的代码中使用::
。通过仅导出我的包的用户所需的函数,我可以将自己的函数保存在NAMESPACE中而不会遇到冲突。未导出的函数也不会隐藏具有相同名称的函数,因此这是双赢。
有关您在此处找到的确切环境,命名空间和工作方式的详细指南: http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
对于每个人都在编写包等等,这绝对是必读的。阅读本文后,您将意识到在包裹代码中使用::
是不必要的。