精简版
对于那些不想阅读我的“案例”的人来说,这就是本质:
- 什么是最小化新包破坏现有代码的机会的推荐方法,即使您编写的代码尽可能健壮?
什么是充分利用命名空间机制的推荐方法?
a) 只使用贡献的包(比如在一些 R 分析项目中)?
b) 关于开发自己的软件包?
由于甚至没有与类(AFAIU)可比的命名空间机制,如何最好地避免与正式类(在我的情况下主要是参考类)相关的冲突?
::
R 宇宙的运作方式
这是我两年来一直在脑海中挥之不去的事情,但我不觉得我已经找到了一个令人满意的解决方案。另外,我觉得情况越来越糟。
我们在CRAN、github、R-Forge等上看到越来越多的包,这简直太棒了。
在这样一个去中心化的环境中,构成 R 的代码库(为简单起见,假设是基础 R和贡献的 R)很自然地会偏离稳健性方面的理想状态:人们遵循不同的约定,有 S3、S4 、S4 参考类等。如果有一个强制执行约定的“中央清算实例”,事情就不能像它们那样“对齐” 。没关系。
问题
鉴于上述情况,使用 R 编写健壮的代码可能非常困难。并非您需要的所有东西都在 base R 中。对于某些项目,您最终会加载相当多的贡献包。
恕我直言,在这方面最大的问题是命名空间概念在 R 中的使用方式:R 允许简单地编写某个函数/方法的名称,而无需明确要求它的命名空间(即foo
vs. namespace::foo
)。
所以为了简单起见,这就是每个人都在做的事情。但这样一来,名称冲突、代码损坏以及需要重写/重构代码只是时间问题(或加载的不同包的数量)。
充其量,您将知道哪些现有功能被新添加的包屏蔽/重载。在最坏的情况下,您将一无所知,直到您的代码中断。
几个例子:
- 尝试同时加载RMySQL和RSQLite,它们不太顺利
- RMongo也会覆盖 RMySQL 的某些功能
- 预测掩盖了很多关于 ARIMA 相关函数的东西
- R.utils甚至掩盖了
base::parse
例程
(我不记得具体是哪些功能导致了问题,但如果有兴趣,我愿意再次查找)
令人惊讶的是,这似乎并没有困扰很多程序员。我试图在r-devel引起几次兴趣,但无济于事。
::
使用运算符的缺点
::
正如 Dominick Samperi指出的那样,在某些情况下使用运算符可能会严重影响效率。- 在开发自己的包时,您甚至不能
::
在自己的代码中使用运算符,因为您的代码还不是真正的包,因此也没有命名空间。所以我必须一开始就坚持下去foo
,构建,测试,然后再把所有东西都改成namespace::foo
. 并不真地。
避免这些问题的可能解决方案
- 将每个包中的每个函数重新分配给遵循某些命名约定的变量,例如
namespace..foo
,为了避免与之相关的低效率(我在这里namespace::foo
概述过一次)。优点:它有效。缺点:它很笨拙,而且你使用的内存加倍。 - 开发包时模拟命名空间。AFAIU,这不太可能,至少我当时被告知。
- 强制使用
namespace::foo
. _ 恕我直言,那将是最好的做法。当然,我们会失去一些简单性,但话说回来,R 宇宙不再简单了(至少它不像 00 年代初期那样简单)。
那么(正式)课程呢?
除了上述方面之外,::
方式对于函数/方法也很有效。但是类定义呢?
将包timeDate与它的类一起使用timeDate
。假设出现另一个包,它也有一个 class timeDate
。我看不出我如何明确声明我想要timeDate
来自两个包中的任何一个的类的新实例。
这样的事情是行不通的:
new(timeDate::timeDate)
new("timeDate::timeDate")
new("timeDate", ns="timeDate")
随着越来越多的人为他们的 R 包切换到 OOP 样式,这可能是一个大问题,从而导致大量的类定义。如果有一种方法可以显式处理类定义的命名空间,我将非常感谢一个指针!
结论
尽管这有点冗长,但我希望我能够指出核心问题/问题,并且我可以在这里提高更多认识。