switch() 函数可以简洁地替代一长串 if ... else if 判断。switch 所依据的变量通常是一个字符串;这种情况下,选择器里可以省略引号。
star <- function(type) {
switch(type,
M = , # will "fall through" if no value given
K = "red dwarf",
G = "Earth-like",
"bigger star" # only correct for O,B,A,F
)
}
> star("M")
[1] "red dwarf"
注意,只有当值留空时,选项才会向下穿透,就像上面例子中的 M。和某些其他语言不同,这里不需要写 break 语句。
最后一个值可以是默认值(如本例所示),也可以是一个 stop(),在条件本应穷尽所有情况时抛出错误。用整数进行 switch 稍有不同:这种情况下默认值始终是 NULL(这一点会在 虚无概念 中讨论)。
dplyr 中的函数base R 里的 switch 相当有限:它只能对单个输入做精确匹配。这在 1993 年 R 首次发布时看似合理,但对现代的用法来说就有些不够用了。
正如 Conditionals 概念中提到的,tidyverse 包集合的设计目标是在不影响向下兼容的前提下,补充(有时甚至替换)base R 的功能。
tidyverse 的各个包还配有出色的现代化文档,点击下面的链接可以了解更多细节。
只要在代码顶部加上 library(dplyr)(针对单个包)或 library(tidyverse)(针对整个包集合),就能把 dplyr 引入作用域。
dplyr 库提供了两个与 switch 相关的额外函数。
recode_values 和 replace_values 函数这两个相关的函数可以进行向量化的、类似 switch 的映射,把旧值映射为新值。
它们的主要区别在于:recode_values() 会创建一个全新的向量,而 replace_values() 允许对已有向量做部分更新。
匹配仍然是精确匹配,不过:
~ 连接,而不是 =。NA 可以被显式匹配(这一点会在 虚无 概念中讨论)。library(dplyr)
x <- c("a", "b", "a", "d", "b", NA, "c", "e", "z")
recode_values(
x,
"a" ~ 1,
"b" ~ 2,
"c" ~ 3,
c("d", "e") ~ 4, # either "d' or "e" will match
NA ~ 0, # matches missing values
default = 100 # note the different syntax for the default
)
#> [1] 1 2 1 4 2 0 3 4 100
你可能会在网上看到有人建议用 case_match() 函数来做向量化的 switch。
该函数已在 dplyr 1.2.0 版(2026 年 2 月)中废弃。现在尝试使用它会收到一条警告,建议改用 recode_values()。
case_when 函数case_when 把 case_match 的语法又推进一步,允许在 ~ 左侧使用任意逻辑表达式。
输入向量(下面例子里的 x)不需要作为实参传入,只要事先定义好就行。
x <- 1:10
case_when(
x < 3 ~ "low",
between(x, 3, 5) ~ "mid",
between(x, 6, 8) ~ "high",
.default = "what?"
)
#> [1] "low" "low" "mid" "mid" "mid" "high" "high" "high" "what?" "what?"
between() 函数也是 dplyr 的一部分。
字符串函数已经讨论过,正则表达式 则是后面某个概念的主题。这两者和 case_when 搭配得非常好。
library(stringr)
tracks <- "R C Python C# Elixir C++ Odin" |> str_split_1(" ")
tracks
#> [1] "R" "C" "Python" "C#" "Elixir" "C++" "Odin"
case_when(
str_starts(tracks, "C") ~ "C-like",
str_starts(tracks, "[AEIOU]") ~ "vowel",
.default = "other"
)
#> [1] "other" "C-like" "other" "C-like" "vowel" "C-like" "vowel"