16.2 信息、警告和错误

我们已经在很多场合下见过print函数,它能把变量显示到控制台。R有三种函数能把程序状态的诊断信息显示出来。根据严重程度由小到大排序,它们分别是messagewarningstop

message能把它所有的输入拼接起来,中间不需要任何的空格,然后把它们都写到控制台。它常用于对长时间运行的函数提供状态更新,或当要改变一个函数时把新的行为通知给用户,又或是提供默认参数的信息:

  1. f <- function(x)
  2. {
  3. message("'x' contains ", toString(x))
  4. x
  5. }
  6. f(letters[1:5])
  7. ## 'x' contains a, b, c, d, e
  8. ## [1] "a" "b" "c" "d" "e"

比起print(或更低级的cat),消息(message)函数的主要优点是用户可以关闭其显示。虽然这看似微不足道,但是当你要重复运行相同的代码时,就可以避免总是不断地看到同样的消息,令你倍受鼓舞:

  1. suppressMessages(f(letters[1:5]))
  2. ## [1] "a" "b" "c" "d" "e"

警告(warning)的行为与消息非常相似,但它还有一些额外的特性能特别指出坏消息。警告适用于以下情景:当系统出了问题,但并非错得离谱以致于你的代码放弃执行。常见的例子是:糟糕的用户输入、较差的数值精度,或意想不到的副作用:

  1. g <- function(x)
  2. {
  3. if(any(x < 0))
  4. {
  5. warning("'x' contains negative values: ", toString(x[x < 0]))
  6. }
  7. x
  8. }
  9. g(c(3, -7, 2, -9))
  10. ## Warning: 'x' contains negative values: -7, -9
  11. ## [1] 3 -7 2 -9

和消息一样,警告可以被抑制:

  1. suppressWarnings(g(c(3, -7, 2, -9)))
  2. ## [1] 3 -7 2 -9

有一个全局选项warn,它确定如何处理警告。默认情况下warn取值为0,这意味着只有当你的代码运行完毕,警告才会出现。

使用getOption可见warn选项的当前级别:

  1. getOption("warn")
  2. ## [1] 1

如果该值小于零,所有的警告都将被忽略:

  1. old_ops <- options(warn = -1)
  2. g(c(3, -7, 2, -9))
  3. ## [1] 3 -7 2 -9

但通常来说,完全关闭警告是很危险的,所以你应该使用以下命令把选项重置为之前的状态:

  1. options(old_ops)

warn设置为1意味着只要发生了警告就马上显示它们,如果值为2或其他更大的值则意味着所有的警告都将变成错误。

可以通过输入last.warning访问最后发生的警告。

我们在前面曾提到,如果warn选项设置为0,那么当你的代码运行完成后才会显示警告。其实这稍微有点复杂。如果只生成了10个或更少的警告,那么之前的说法完全正确。但是如果警告超过10个,你会得到一个消息,说明一共已经生成了多少次警告,而你也必须键入warnings()来查看它们。如图16-1所示。

图像说明文字

图16-1:警告出现次数多于10个时,使用warning来查看它们

错误(error)是最严重的情况,且会停止程序的执行。错误应该只用于错误发生时,或者你知道错误将会发生时。常见的原因包括无法纠正的(例如,通过as.*函数)错误输入,无法读取或写入文件,或严重的数值误差:

  1. h <- function(x, na.rm = FALSE)
  2. {
  3. if(!na.rm && any(is.na(x)))
  4. {
  5. stop("'x' has missing values.")
  6. }
  7. x
  8. }
  9. h(c(1, NA))
  10. ## Error: 'x' has missing values.

如果任何传递给stopifnot的表达式被判定为假时,它会抛出一个错误。这里提供了一个简单的方法来检查程序的状态是否符合预期:

  1. h <- function(x, na.rm = FALSE)
  2. {
  3. if(!na.rm)
  4. {
  5. stopifnot(!any(is.na(x)))
  6. }
  7. x
  8. }
  9. h(c(1, NA))
  10. ## Error: !any(is.na(x)) is not TRUE

对于更广泛的人性化的测试,使用assertive包:

  1. library(assertive)
  2. h <- function(x, na.rm = FALSE)
  3. {
  4. if(!na.rm)
  5. {
  6. assert_all_are_not_na(x)
  7. }
  8. x
  9. }
  10. h(c(1, NA))
  11. ## Error: x contains NAs.