Introduction

在 R 中,包(packages)是可分享 R 代码的基本单位。 一个 package 将代码(code)、数据(data)、文档(documentation)和测试(tests)整合到一起,很容易与他人分享。 截止到 2023 年 3 月,在综合 R 包存档网络 CRAN(Comprehensive R Archive Network),R packages 的公共发布平台上已经有超过 19,000 多个可用的 packages。 拥有这么众多的 packages 是 R 成功的原因之一:也许有人已经解决了您正在尝试解决的问题,因此您可以通过下载他们的 package 来从他们的工作中获益。

如果您正在阅读这本书,那么你应该已经知道如何使用 packages 了:

本书的目的是教您如何开发程序包,这样您就可以编写自己的程序包,而不仅仅是使用他人的程序包。 为什么我们要写程序包呢? 一个令人信服的理由是,您想要与他人分享自己的代码。 将您的代码整合到一个程序包中能让他人更加轻松地使用它,因为和您一样,他们也知道如何使用包。 如果您的代码放在包中,任何 R 的用户都可以便利地下载、安装并且学会如何使用。

但对您来说,即使从不共享代码,包也很有用。 正如 Hilary Parker 在 introduction to packages 中所说:“Seriously, it doesn’t have to be about sharing your code (although that is an added benefit!). It is about saving yourself time.” 将您的代码组织在程序包中能够使工作变得更轻松,这是因为包有一些通用的约定。 比如,你需要将 R code 放在 R/ 目录下,将 tests 放在 tests/ 目录下,还有将 data 放在 data/ 目录下。 这些约定很有用,因为:

甚至可以使用包来构建你的数据分析(e.g., Marwick, Boettiger, and Mullen (2018a) or Marwick, Boettiger, and Mullen (2018b)),尽管我们不会在这里深入研究该用例。

Philosophy

这本书支持了我们的程序包开发理念:任何能够自动化的东西都应该自动化。 尽量减少手动操作。 用函数完成尽可能多的事情。 这样是希望您将时间用于思考您想要包做什么工作,而不是包结构的各种细节。

这种开发理念主要是通过 devtools 包来实现的,这个程序包是让通用开发任务自动化的 R 函数套件中的代表。 devtools 在 2018 年 10 月发布了 2.0.0 版本,这标志着其内部结构重组为一系列功能更具针对性的程序包,而它则更像是一个元程序包(meta-package)。 usethis 程序包是您最有可能与之交互的子程序包;我们将在 Section 2.2 解释 devtools 与 usethis 之间的关系。

像往常一样,devtools 程序包的目的是让程序包的开发尽可能的轻松便利。 它囊括了第一作者 Hadley Wickham 这些年来作为一名多产的独立开发者的最佳实践经验。 最近,他在 Posit(以前称为 RStudio)组建了一个开发团队,他们共同管理着数百个开源 R 包,包括那些被称为 the tidyverse 的包。 这个团队的影响力使得我们能够在一个巨大的规模上探索所有可能出现的错误与问题。 幸运的是,它还让我们有机会与专家和友善的同事一起反思成功与失败。 我们尝试找到一些能够使包的维护者和使用者更快乐地工作生活的做法。 而正是在 devtools meta-package 里,您将看到这些方法是如何具体实现的。

devtools 与 RStudio 联系紧密、携手共进,而后者正是我们认为对大多数 R 用户而言最佳的开发环境。 目前最流行的 RStudio 替代品是启用了 R extensionVisual Studio Code (VS Code)。 这可能是一个有益而强大的环境,但是它确实需要更多的工作来设置和定制1

RStudio

在整本书中,我们在像这样的特殊格式部分中强调了 RStudio 可以加快您的包开发工作流程的具体方法。

devtools 和 RStudio 一起,让您无需关注包是怎样建立的这种低级细节。 但是当您开始开发更多的程序包时,我们强烈建议你去了解这些细节。 有关软件包开发细节的最好的官方资源,始终是官方的 writing R extensions。 然而,如果您不熟悉包的基础知识,是很难理解这本手册的。 它也很详尽,涵盖了所有可能的包的组件,而不是像本书一样专注于那些最常见和最有用的组件。 一旦你掌握了 R 包的基础知识,并且想深入了解更多的知识,那么官方的 writing R extensions 将是一个很有用的资源。

In this book

本书的第一部分是为您提供开始包开发之旅所需的所有工具,我们强烈建议您按顺序阅读。 我们从 Chapter 1 开始,介绍了一个小软件包的完整开发过程。 这是为了在我们深入研究 R 包的关键组件之前,描绘开发的大图景并提出对一个工作流程的建议。 然后,在 Chapter 2 中,您将学习如何为包开发准备好你的系统,在 Chapter 3 中,您将了解包的基本结构以及它在不同状态下的变化。 接下来,在 Chapter 4 中,我们将介绍软件包开发人员反复提出的核心工作流程。 本书的第一部分以另一个案例研究(Chapter 5)结尾,这次重点介绍如何将 script 转换为 package,并讨论在此过程中您将面临的挑战。

本书的其余部分旨在根据需要阅读。 随着开发过程中出现的各种主题,在章节之间进行选择。

首先,我们介绍关键的包组件:Chapter 6 讨论代码所在的位置以及如何组织代码,Chapter 7 向您展示如何在包中包含数据,Chapter 8 介绍一些需要在某处讨论的不太重要的文件和目录。

接下来,我们将从 Chapter 9 中的 DESCRIPTION 开始深入研究 package metadata。 然后我们将深入研究 dependencies。 在 Chapter 10 中,我们将介绍 dependencies 的成本和好处,并提供一些有关包命名空间(package namespaces)和搜索路径(search path)的技术背景。 在 Chapter 11 中,我们关注实际问题,例如如何在包的不同部分中使用不同类型的 dependencies。 这也是我们讨论导出函数(exporting functions)的地方,这使得其他包和项目可以依赖于您的包。 我们将在 Chapter 12 中讨论许可(licensing)来结束这一部分。

为了确保您的包按设计工作(并在您进行更改时继续工作),测试代码至关重要,因此接下来的三章将介绍测试的艺术和科学。 Chapter 13 将带您开始使用 testthat 包进行测试的基础知识。 Chapter 14 教您如何以最有效的方式设计和组织测试。 然后我们在 Chapter 15 中完成了测试的介绍,该章教您应对具有挑战性的情况的高级技能。

如果您希望其他人(包括未来的您!)了解如何使用包中的功能,您需要记录它们。 Chapter 16 让您开始使用 roxygen2 来记录包中的函数。 仅当您知道要查找哪些函数时,函数文档才有用,因此接下来在 Chapter 17 中我们将讨论小插曲(vignettes),它可以帮助您将包作为一个整体进行记录。 我们将在 Chapter 18 中讨论其他重要的 Markdown 文件(例如 README.mdNEWS.md),并在 Chapter 19 中使用 pkgdown 创建包网站来完成文档。

本书最后回顾了开发实践,例如使用版本控制和持续集成的好处(Chapter 20)。 我们通过讨论包的生命周期(Chapter 21)来总结,包括在 CRAN 上发布它(Chapter 22)。

有很多东西需要学习,但不要感到不知所措。 从一些有用功能的最小一部分开始(例如,只是一个 R/ 目录!),并随着时间的推移逐步建立并完善它。 套用禅师 Shunryu Suzuki 的话:“Each package is perfect the way it is — and it can use a little improvement”。

What’s not here

还有一些特定的实践在这里很少或根本没有处理,仅仅是因为我们没有足够地使用它们来获得任何特殊的洞察力。 这是否意味着我们积极劝阻这些做法? 可能不会,因为我们试图明确说明我们认为您应该避免的做法。 因此,如果此处未涵盖某些内容,则仅意味着构建了数百个频繁使用的 R 包,而没有对该技术产生有意义的依赖。 这种观察应该会激励您评估您的开发需求与我们的真正不重叠的可能性有多大。 但有时答案是明确的“是”,在这种情况下,您只需要咨询其他资源即可。


  1. Emacs Speaks Statistics (ESS) 的用户会发现本书中描述的许多工作流程也可以在那里使用。对于那些忠实于 vim 的用户,我们推荐 Nvim-R plugin↩︎