Menu

R 语言 if else:if、else if、ifelse() 与 switch()

R 语言中的条件逻辑:if/else if/else 语法、让脚本出错的花括号陷阱、作用于整个向量的向量化 ifelse(),以及处理多分支选择的 switch()。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

if 语句

R 中的 if 语句只在条件为 TRUE 时运行一段代码。条件写在圆括号里,代码写在花括号里,可选的 else 则说明否则该做什么:

条件 score >= 60 的结果是单个 TRUE,所以运行第一个分支。构造这些条件用的就是比较运算符和逻辑运算符,比如 ==、>=、&&、%in% 等。

如果结果多于两种,就用 else if 串联多个判断。R 从第一个条件开始依次往下检查,运行第一个条件为 TRUE 的分支,并跳过其余分支:

顺序很重要:因为 85 >= 80 在 85 >= 70 之前检查,所以成绩是 "B"。如果把判断顺序反过来,所有 70 分以上的成绩都会得到 "C"。

花括号陷阱:else 必须放在哪里

几乎每个人写第一个 R 脚本时都会踩到这个错误。在脚本中(或者任何用 Rscript 运行的代码中),else 必须和前一个分支的右花括号写在同一行。下面的写法会出错:

if (score >= 60) {
  print("You passed")
}
else {              # Error: unexpected 'else'
  print("Try again")
}

原因是:R 按语句逐条解析。当它读到行首的 } 时,if 语句在语法上已经完整,R 会立即求值。下一行的 else 就成了一条新语句的开头,而语句不能以 else 开头。解决办法就是本页通篇使用的写法:

if (score >= 60) {
  print("You passed")
} else {            # brace and else on the same line
  print("Try again")
}

在函数体或者任何外层花括号内部,这条严格规则会放宽(R 会一直读到外层花括号闭合为止),但不要依赖这一点。} else { 的写法在任何地方都能用,也是 R 代码风格指南所规定的写法。

条件必须是一个 TRUE 或 FALSE

if() 要求恰好一个逻辑值。有两种情况会违反这个要求,而且都会报错:

if (NA) print("hi")
# Error: missing value where TRUE/FALSE needed

if (c(TRUE, FALSE)) print("hi")
# Error: the condition has length > 1

NA 这种情况在实际中很重要,因为涉及缺失数据的比较会产生 NA:如果 x 是 NA,那么 x > 10 也是 NA,把这样的条件交给 if,你的脚本就会停下来。防御性的写法是使用短路求值的 &&:

因为 !is.na(x) 是 FALSE,所以 x > 10 这部分根本不会被求值,条件里也就不会出现 NA。NA 如何在比较中传播,请参阅缺失值。

长度大于 1 的错误(从 R 4.2 开始强制执行;在此之前 R 会悄悄使用第一个元素)通常说明你真正想要的是下面几种之一:只要有一个满足就够了,用 any(v > 5);每个元素都必须满足,用 all(v > 5);如果你想要逐元素的答案,就用 ifelse(),也就是下一节的内容。

ifelse():向量化的 if else

if 只做一次判断。ifelse(test, yes, no) 则对向量的每个元素都做判断,并返回一个长度相同的向量:

temps 的每个元素都被独立检查:"hot" "mild" "mild" "hot" "mild"。从已有的向量或数据框列派生出新的一列,用的就是这个工具,不需要写循环。

和 if 不同,ifelse() 能妥善处理缺失值:检查中的 NA 会在结果中产生 NA,而不是报错:

你可以嵌套 ifelse() 调用来做三分支的划分,到这个层次代码依然可读:

但到此为止。嵌套三层或更多的 ifelse() 会变成配对括号的谜题;这时应该改用 cut()(针对数值范围)或者 dplyr::case_when(),后者把每个条件写在单独的一行。

用 switch() 处理多分支选择

当你要把一个值和一组已知的名称做匹配(而不是检查范围)时,冗长的 else if 链就显得笨拙了。switch() 可以直接表达这种意图:

需要了解的三种行为:

  • 匹配:switch() 把字符值和参数名进行比较,并返回匹配到的结果。
  • 贯穿:右边为空的名称(sat = ,)会贯穿到下一个情况的结果,"sat" 和 "sun" 就是这样共用 "weekend",而无需重复书写。
  • 默认值:当没有任何匹配时,返回最后一个没有名称的参数("weekday")。如果没有默认值,未匹配的值会让 switch() 不可见地返回 NULL,你的函数悄无声息地什么也不返回,这是一种令人困惑的 bug。一定要提供默认值(或者明确地写成 stop())。

switch() 最适合用在根据模式或方法参数进行分派的函数里;凡是基于数值阈值的判断,还是用 else if 或 cut()。

要点回顾

  • if (condition) { } else { }:条件写在圆括号里,只运行一个分支;用 else if 串联,第一个匹配的分支胜出。
  • 在脚本中,把 } else { 写在同一行;else 出现在行首是语法错误。
  • if() 需要恰好一个 TRUE/FALSE:NA 会报错,更长的向量也会报错(从 R 4.2 开始)。用 !is.na(x) && 加以防护,或者用 any()/all() 归约。
  • ifelse(test, yes, no) 是向量化版本:用它计算新的向量,嵌套不要超过两层。
  • switch() 按名称处理多分支分派,用空的情况实现贯穿;一定要给它一个默认值。

下一篇:让代码重复执行,学习 for 循环,以及什么时候适合(和不适合)用循环。

常见问题

R 语言中 if else 语句怎么写?

if (condition) { ... } else { ... }:条件写在圆括号里,每个分支写在花括号里。用 else if (condition) 可以继续串联更多判断。在脚本中,else 必须和上一个分支的右花括号写在同一行(} else {),否则 R 会报 unexpected 'else'。

R 语言中 if 和 ifelse() 有什么区别?

if 是一个流程控制语句,只检查一个 TRUE/FALSE 值,并运行其中一个分支的代码。ifelse(test, yes, no) 是一个向量化函数:它一次性检查向量的每个元素,并返回一个结果向量。要决定程序做什么,用 if;要根据已有的列或向量计算出新的列或向量,用 ifelse()。

R 为什么提示 'the condition has length > 1'?

你把整个向量传给了 if(),而它需要的是单个 TRUE 或 FALSE。从 R 4.2 开始,这会直接报错,而不是悄悄使用第一个元素。解决办法是把向量归约成一个值,比如 any(x > 5)、all(x > 5);如果你想要逐元素的结果,就改用向量化的 ifelse()。

R 语言中 switch() 怎么用?

switch(value, name1 = result1, name2 = result2, default) 会把一个字符值和各个参数名进行比较,并返回匹配到的结果。右边为空的名称会贯穿到下一个结果,因此多个情况可以共用一个结果。最后一个没有名称的参数充当默认值;如果没有默认值,未匹配的值会不可见地返回 NULL。

Coddy 编程语言插图

和 Coddy 一起学编程

开始