短版

对于那些不想通读我的" case "的人来说,这是关键:

  1. 建议采用什么方法来最大限度地减少新包 destruct 现有代码的可能性,即使您编写的代码达到as robust as possible
  2. 什么是最好地利用namespace mechanism的推荐方式

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

    b) 关于developing个自己的包裹?

  3. 如何最好地避免与formal classes(在我的例子中主要是Reference Classes)的冲突,因为对于类(AFAIU)甚至没有一个与::类似的命名空间机制?


R宇宙的运作方式

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

我们看到,CRANgithubR-Forge等的软件包数量不断增加,这简直太棒了.

在这样一个分散的环境中,组成R的代码库(为了简单起见,假设是base Rcontributed R)自然会偏离健壮性方面的理想状态:人们遵循不同的约定,有S3、S4、S4引用类,等等.如果有一个"central clearing instance"强制执行约定,事情就不会像它们那样"一致".没关系.

问题

鉴于上述情况,使用R编写健壮的代码可能非常困难.并不是所有你需要的东西都在base R中.对于某些项目,你最终会加载相当多的贡献包.

IMHO, the biggest issue in that respect is the way the namespace concept is put to use in R: R allows for simply writing the name of a certain function/method without explicitly requiring it's namespace (i.e. 100 vs. 101)

所以为了简单起见,每个人都在这么做.但这样一来,名称冲突、代码中断和重写/重构代码的需要只是时间问题(或加载的不同包的数量).

充其量,您将了解哪些现有函数被新添加的包屏蔽/重载.在最坏的情况下,在代码中断之前,您将没有任何线索.

举几个例子:

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

令人惊讶的是,这似乎并没有困扰很多程序员.我试着在r-devel岁时提高几次兴趣,但都没有什么效果.

Downsides of using the :: operator

  1. 在某些情况下,使用::运算符可能会显著降低效率,如Dominick Samperi pointed out.
  2. 当您创建自己的包时,您甚至不能在自己的代码中使用::运算符,因为您的代码还不是真正的包,因此也没有名称空间.因此,我首先必须坚持foo方式,构建、测试,然后回到将所有内容更改为namespace::foo.不是真的.

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

  1. Reassign将每个包中的每个函数转换为遵循特定命名约定的变量,例如namespace..foo,以避免与namespace::foo相关的低效率(我曾在here中对其进行了概述).优点:很有效.缺点:这很笨拙,而且你使用的内存增加了一倍.
  2. Simulate在开发包时使用名称空间.阿福,这真的不可能,至少我当时told so back then岁.
  3. 使用namespace::foo使其成为mandatory.嗯,那是最好的办法.当然,我们会失go 一定程度的简单性,但R宇宙也不再简单(至少不像20世纪初那么简单).

那么(正式)课程呢?

除了上述方面之外,::-way对于函数/方法也非常有效.但是关于类定义呢?

带上timeDate班的timeDate号Package.假设另一个包也有timeDate类.我看不出我如何能明确地声明我希望从这两个包中的任何一个获得类timeDate的新实例.

这样的事情是行不通的:

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

这可能是一个巨大的问题,因为越来越多的人在R包中使用OOP风格,导致了大量的类定义.如果有一种方法可以显式地处理类定义的名称空间,我将非常感谢一个指针!

结论

尽管这有点冗长,但我希望我能够指出核心问题,并在这里提高更多意识.

我认为devtoolsmvbutils确实有一些值得推广的方法,但我相信还有更多要说的.

推荐答案

问得好.

验证

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

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

  1. 避免使用依赖的软件包,而偏爱使用导入的软件包.仅在导入中填充依赖项的包是完全安全的.如果你必须使用一个软件包,那么在你拨打install.packages()后立即给作者发邮箱.

以下是我告诉作者的:"作者您好,我是XYZ软件包的粉丝.我想提出一个请求.您能在下一次更新中将ABC和DEF从Dependes移动到Imports吗?在这种情况发生之前,我无法将您的软件包添加到我自己的软件包的Imports中.由于R 2.14 for each 软件包强制使用名称空间,R Core的一般信息是,软件包应该尽量做到"好"公民".如果我必须加载Dependes软件包,这会增加一个巨大的负担:每次我依赖一个新软件包时,我都必须判断是否存在冲突.对于导入,软件包没有副作用.我理解这样做可能会 destruct 其他人的软件包.我认为这样做是正确的,以表明对导入的promise 从长远来看,它将帮助人们产生更健壮的R代码."

  1. 使用importFrom.不要将整个软件包添加到导入中,只添加您需要的特定功能.我通过Roxygen2函数文档和roxygenize()来实现这一点,roxygenize()会自动生成名称空间文件.通过这种方式,您可以导入两个有冲突的包,而这些冲突并不在您实际需要使用的函数中.这无聊吗?直到它成为一种习惯.好处:您可以快速识别所有的第三方依赖关系.这有助于...

  2. 不要盲目升级软件包.逐行阅读CeleLogo,并考虑更新将如何影响您自己的包的 solidity .大多数情况下,更新不会触及您实际使用的功能.

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

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

  5. 无论何时构建/测试软件包,都要分析输出并查找注释、信息和警告.另外,用自己的眼睛进行身体扫描.构建步骤中有一部分会记录冲突,但不会附加警告等.

  6. 在调用第三方软件包后立即添加断言和不变量.换句话说,不要完全相信别人给你的.稍微探测一下结果,如果结果出乎意料,则探测stop()次.你不必发疯—— Select 一两个暗示有效/高置信度结果的断言.

我想还有更多,但现在这已经成为肌肉记忆了(如果更多,我会增强).

R相关问答推荐

通过绘图 Select 线串几何体并为其着色

将模拟变量乘以多个观测结果中的模拟变量

查找满足SpatRaster中条件的单元格位置

如何使用stat_extract_all正确提取我的目标值?

S用事件解决物质平衡问题

为什么当用osmdata映射R时会得到相邻状态?

将. xlsx内容显示为HTML表

如何提取所有完美匹配的10个核苷酸在一个成对的匹配与生物字符串在R?>

哪一行和行和 Select 特定行,但是考虑到Nas

Geom_Hline将不会出现,而它以前出现了

如何计算R glm probit中的线性预测因子?

如何移除GGPlot中超出与面相交的任何格网像元

将统计检验添加到GGPUBR中的盒图,在R

R中时间间隔的大向量与参考时间间隔的相交

访问数据帧中未定义的列时出现R错误

使用geom_sf跨越日期线时的闭合边界

如何根据其他列中的两个条件来计算数据帧中的行之间的差异?

如何在R曲线图弹出窗口中更改r和theta标签

如何在分组蜂群小区中正确定位标签

如何在类应用函数中访问函数本身