17.8 包的维护

函数对用户来说就像是一个黑盒子。他们会给它传递一些参数然后函数也会返回一些值,用户不需要关心函数内部发生了什么(至少理论上如此)。这意味着,一个函数的签名(即函数的名称及参数的顺序)不应该没有警告用户就直接更改。R提供了一些函数能帮助通知用户签名已改变。

如果你正计划增加一个新的功能但还没有机会实现它,或想提前通知你的用户此功能将至,可以使用.NotYetUsed函数。如果用户试图过早地使用它,这将抛出一个错误或警告,理由是它还不能使用。在下例中,我们将为hypotenuse函数添加一个二维的p范数。在添加新的功能之前,我们只需更改其签名,并且在用户尝试使用p参数时抛出一个错误:

  1. hypotenuse <- function(x, y, p = 2)
  2. {
  3. if(!missing(p))
  4. {
  5. .NotYetUsed("p")
  6. }
  7. sqrt(x ^ 2 + y ^ 2)
  8. }
  9. hypotenuse(5, 12) # 行为与之前的一样
  10. ## [1] 13
  11. hypotenuse(5, 12, 1)
  12. ## Error: argument 'p' is not used (yet)

一旦我们添加了新的功能,可以删除.NotYetUsed

  1. hypotenuse <- function(x, y, p = 2)
  2. {
  3. (x ^ p + y ^ p) ^ (1 / p)
  4. }

如果你想添加一个全新的功能(而不仅仅是一个参数),对应的函数是.NotYetImplemented。当你第一次创建包或添加一大块功能时这非常适用。编写单个函数会非常耗时,所以当写完一些代码,你可能已忘记其他本来要添加的函数。因此,有时最好是先完成上层的设计,然后再逐步实现具体的底层细节。只需简单地为每个函数先创建一个占位符,在函数体中先使用.NotYetImplemented代替。下例中的函数将会在以后某一天才开始计算三角数,不过它现在只会抛出一个错误:

  1. triangular <- function(n)
  2. {
  3. .NotYetImplemented()
  4. }
  5. triangular()
  6. ## Error: 'triangular' is not implemented yet

如果你想删除一个函数,最礼貌的做法是是分阶段完成。第一步是在函数内添加一个.Deprecated的调用,其参数为它所要替代的函数的名称。该函数的其余部分应保持不变,从而能够保留现有的行为:

  1. hypotenuse <- function(x, y, p = 2)
  2. {
  3. .Deprecated("p_norm")
  4. (x ^ p + y ^ p) ^ (1 / p)
  5. }
  6. hypotenuse(5, 12)
  7. ## Warning: 'hypotenuse' is deprecated. Use 'p_norm' instead. See
  8. ## help("Deprecated")
  9. ## [1] 13

经过一段适当长的时间——足以让你的用户注意到相关函数过时的消息,你就可以改变函数的内容,让它调用.Defunct,这将抛出一个错误:

  1. hypotenuse <- function(x, y, p = 2)
  2. {
  3. .Defunct("p_norm")
  4. }
  5. hypotenuse(5, 12)
  6. ## Error: 'hypotenuse' is defunct. Use 'p_norm' instead. See help("Defunct")