Advanced R学习笔记(四)Control flow
介绍
有两种主要的控制结构,选择和循环.选择:像是if和switch().允许根据不同的输入执行不同的代码.循环: 像是for和while,允许重复执行代码,通常使用变化的选项.
选择
在R中是这样子的
if (condition) true_action
if (condition) true_action else false_action
通常操作是被{包括
grade <- function(x) {
if (x > 90) {
"A"
} else if (x > 80) {
"B"
} else if (x > 50) {
"C"
} else {
"F"
}
}
if返回一个值以便于分配这个结果
x1 <- if (TRUE) 1 else 2
x2 <- if (FALSE) 1 else 2
c(x1, x2)
#> [1] 1 2
建议当if只有一行时才进行赋值,否则会很难读懂
当不使用else的时候,if将会在条件是错的时候返回NULL,c()和paste()会略去NULL的输入,使得更加简洁.
greet <- function(name, birthday = FALSE) {
paste0(
"Hi ", name,
if (birthday) " and HAPPY BIRTHDAY"
)
}
greet("Maria", FALSE)
#> [1] "Hi Maria"
greet("Jaime", TRUE)
#> [1] "Hi Jaime and HAPPY BIRTHDAY"
无效的输入
条件应是单独的TRUE或者FALSE,大部分其他的输入将会产生一个错误.
if ("x") 1
#> Error in if ("x") 1:
#> argument is not interpretable as logical
if (logical()) 1
#> Error in if (logical()) 1:
#> argument is of length zero
if (NA) 1
#> Error in if (NA) 1:
#> missing value where TRUE/FALSE needed
if ("x") 1
#> Error in if ("x") 1:
#> argument is not interpretable as logical
if (logical()) 1
#> Error in if (logical()) 1:
#> argument is of length zero
if (NA) 1
#> Error in if (NA) 1:
#> missing value where TRUE/FALSE needed
当一个长度大于一的逻辑向量则只会产生一个警告
if (c(TRUE, FALSE)) 1
#> Warning in if (c(TRUE, FALSE)) 1: the condition has length > 1 and only the
#> first element will be used
#> [1] 1
在R3.5之后可以通过设置环境变量使得这返回一个错误
Sys.setenv("_R_CHECK_LENGTH_1_CONDITION_" = "true")
if (c(TRUE, FALSE)) 1
#> Error in if (c(TRUE, FALSE)) 1:
#> the condition has length > 1
Vectorised if
当想操纵长度不为一的向量时,可以使用ifelse(),给出的参数是测试,是的情况下的操作和错误情况下的操作
x <- 1:10
ifelse(x %% 5 == 0, "XXX", as.character(x))
#> [1] "1" "2" "3" "4" "XXX" "6" "7" "8" "9" "XXX"
ifelse(x %% 2 == 0, "even", "odd")
#> [1] "odd" "even" "odd" "even" "odd" "even" "odd" "even" "odd" "even"
缺失值将会被传播到输出
建议在输出时两种输出同种类型时才使用ifelse(),否则很难预料输出什么类型的对象.
其他的向量化的if可以用dplyr::case_when()
dplyr::case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
is.na(x) ~ "???",
TRUE ~ as.character(x)
)
#> [1] "1" "2" "3" "4" "fizz" "6" "buzz" "8" "9" "fizz"
switch()语句
更像if 是一个紧凑的版本.
x_option <- function(x) {
if (x == "a") {
"option 1"
} else if (x == "b") {
"option 2"
} else if (x == "c") {
"option 3"
} else {
stop("Invalid `x` value")
}
}
可以这么写
x_option <- function(x) {
switch(x,
a = "option 1",
b = "option 2",
c = "option 3",
stop("Invalid `x` value")
)
}
最后一行应该给出一个错误,否则就会给出不可见的NULL
(switch("c", a = 1, b = 2))
#> NULL
如果多个输入有同一个输出,可以将等号右边空置,它将会滑向下一个值
legs <- function(x) {
switch(x,
cow = ,
horse = ,
dog = 4,
human = ,
chicken = 2,
plant = 0,
stop("Unknown input")
)
}
legs("cow")
#> [1] 4
legs("dog")
#> [1] 4
但是只建议对于字符输入使用switch(),对于数字会很难阅读,而且可能在非整数时出现一些未知的错误
循环
for循环用于遍历向量中的每一项.基础形式如下
for (item in vector) perform_action
for (i in 1:3) {
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
但是要注意for会把item赋值给当前环境,覆盖已经存在的同一个名字的变量
i <- 100
for (i in 1:3) {}
i
#> [1] 3
有两种方法去提前结束循环
-
next 退出当前迭代
-
break 退出整个循环
for (i in 1:10) {
if (i < 3)
next
print(i)
if (i >= 5)
break
}
#> [1] 3
#> [1] 4
#> [1] 5
常见的错误
首先,如果已经产生了一个树精,应该预先产生一个容纳它的对象,否则循环会非常慢
means <- c(1, 50, 20)
out <- vector("list", length(means))
for (i in 1:length(means)) {
out[[i]] <- rnorm(10, means[[i]])
}
其次,要小心1:length(x).如果有x长度为0时
means <- c()
out <- vector("list", length(means))
for (i in 1:length(means)) {
out[[i]] <- rnorm(10, means[[i]])
}
#> Error in rnorm(10, means[[i]]):
#> invalid arguments
这是因为:同时适用于递增和递减的序列,可以使用seq_along()
seq_along(means)
#> integer(0)
out <- vector("list", length(means))
for (i in seq_along(means)) {
out[[i]] <- rnorm(10, means[[i]])
}
最后,在循环S3类的时候可能会遇到问题,因为循环会去掉属性
xs <- as.Date(c("2020-01-01", "2010-01-01"))
for (x in xs) {
print(x)
}
#> [1] 18262
#> [1] 14610
可以通过调用自己来解决
for (i in seq_along(xs)) {
print(xs[[i]])
}
#> [1] "2020-01-01"
#> [1] "2010-01-01"
相关的工具
如果提前知道要迭代的数据,那么for很有用,否则可以使用
-
while(condition) action 执行action 当condition是TRUE时
-
repeat(action) 重复action永远