Skip to content

R - 循环

R - 控制流:循环及其现代替代方案

Section titled “R - 控制流:循环及其现代替代方案”

在编程中,我们经常需要多次重复一项任务。循环允许我们重复执行一段代码块。虽然 R 拥有与其他语言类似的传统循环,但它也提供了更高效、更易读的替代方案,如向量化和函数式编程,这些方法通常更受青睐。

本教程将介绍标准循环 (for, while, repeat),然后引入处理重复任务的现代“R 原生”方式。

一个 for 循环会遍历序列(如向量或列表)中的每个项。当您确切知道需要迭代多少次时,它非常有用。

# A for loop that iterates through a vector of numbers
for (i in 1:5) {
# The code inside the curly braces is the 'loop body'
print(paste("The current number is:", i))
}
# Output:
# [1] "The current number is: 1"
# [1] "The current number is: 2"
# ... and so on up to 5

只要指定的条件为 TRUE,while 循环就会重复执行。当您事先不知道确切的迭代次数时,它很有用。

counter <- 1
while (counter <= 5) {
print(paste("Counter is at:", counter))
counter <- counter + 1 # CRITICAL: You must change the condition variable inside the loop!
}
# Output:
# [1] "Counter is at: 1"
# [1] "Counter is at: 2"
# ... and so on up to 5

常见错误: 忘记更新 while 条件中的变量(例如 counter <- counter + 1)将导致无限循环,这会使您的 R 会话崩溃。

一个 repeat 循环默认会创建无限循环。您必须提供一个显式的 break 语句来退出它。它不太常见,但当退出条件复杂且最好在循环中间检查时,它会很有用。

counter <- 1
repeat {
print(paste("Repeat loop counter:", counter))
if (counter >= 5) {
break # Exit the loop
}
counter <- counter + 1
}
  • break:立即终止整个循环。
  • next:跳过当前迭代的剩余部分,并进入下一次迭代。
# Example of 'next' and 'break'
for (i in 1:10) {
if (i %% 2 != 0) {
next # Skip odd numbers
}
if (i > 8) {
break # Stop if the number is greater than 8
}
print(i)
}
# Output:
# [1] 2
# [1] 4
# [1] 6
# [1] 8

“R 方式”:循环的现代替代方案

Section titled ““R 方式”:循环的现代替代方案”

在 R 中,循环可能会很慢,尤其是处理大型数据集时。R 的优势在于向量化和函数式编程,它们更快,而且通常更简洁。

向量化意味着一次性对整个向量应用操作,而不是逐个元素地应用。R 对此进行了高度优化。

my_numbers <- c(10, 20, 30, 40, 50)
# The slow, loop-based way
result_loop <- numeric(length(my_numbers))
for (i in 1:length(my_numbers)) {
result_loop[i] <- my_numbers[i] * 2
}
# The fast, vectorized R way
result_vectorized <- my_numbers * 2
# Both produce the same result, but the vectorized way is far superior.
# result_vectorized is c(20, 40, 60, 80, 100)

当您需要对列表或向量的每个元素应用更复杂的函数时,purrr 包(tidyverse 的一部分)中的 map() 函数是现代标准。它们比基础 R 的 apply 函数族更一致且可预测。

library(purrr)
my_list <- list(a = 1:5, b = c(10, 20), c = 100)
# Use map() to get the mean of each element. The result is a list.
list_of_means <- map(my_list, mean)
# Result: list(a = 3, b = 15, c = 100)
# Use map_dbl() to get the mean and return a numeric vector.
vector_of_means <- map_dbl(my_list, mean)
# Result: c(a = 3, b = 15, c = 100)

尽可能努力使用向量化操作或 purrr::map 函数。然而,在某些情况下,for 循环仍然完全适用且必要:

  • 顺序依赖: 当一次迭代的结果依赖于前一次迭代的结果时(例如,在某些模拟中)。
  • 副作用: 当循环的主要目的是产生副作用时,例如打印到控制台、绘制图表或保存文件。
  • 复杂逻辑: 当逻辑过于复杂而无法整齐地适配向量化或函数式形式时,循环可能更具可读性。