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。