文章目录

R 循环:for、while、repeat

发布于 2026-04-05 11:05:52 · 浏览 19 次 · 评论 0 条

R 循环:for、while、repeat

在 R 语言中,循环是处理重复任务的核心工具。当你需要对数据集中的每一行执行相同操作,或者重复执行计算直到满足某个条件时,循环就显得尤为重要。本文将详细介绍 R 语言的三种循环结构:for 循环、while 循环和 repeat 循环,帮助你根据不同场景选择最合适的循环方式。


for 循环:已知次数的重复

for 循环是最常用的循环结构,适用于你知道需要迭代多少次的情况。它会遍历一个向量或列表中的每一个元素,对每个元素执行相同的代码块。

基本语法

for (变量 in 向量/列表) {
  # 需要重复执行的代码
}

实用示例

遍历向量元素:假设你有一个成绩向量,需要计算每个成绩加 5 分后的新值。

scores <- c(85, 90, 78, 92, 88)

for (score in scores) {
  new_score <- score + 5
  print(new_score)
}

输出结果:

[1] 90
[1] 95
[1] 83
[1] 97
[1] 93

使用索引遍历:有时候你需要同时获取元素的值和位置,这时可以使用索引。

fruits <- c("苹果", "香蕉", "橙子", "葡萄")

for (i in 1:length(fruits)) {
  # 使用 paste0 连接字符串,生成序号和水果名称
  result <- paste0("第", i, "个水果是: ", fruits[i])
  print(result)
}

输出结果:

[1] "第1个水果是: 苹果"
[1] "第2个水果是: 香蕉"
[1] "第3个水果是: 橙子"
[1] "第4个水果是: 葡萄"

嵌套循环:处理多维数据时常常需要嵌套循环。

# 创建一个 3x3 的矩阵
matrix_data <- matrix(1:9, nrow = 3)

for (i in 1:nrow(matrix_data)) {
  for (j in 1:ncol(matrix_data)) {
    # 打印每个元素的坐标和值
    cat("位置 (", i, ",", j, ") 的值是: ", matrix_data[i, j], "\n", sep = "")
  }
}

while 循环:条件驱动的重复

while 循环会在指定条件为 TRUE 时持续执行代码块。它适用于你不知道需要循环多少次,但知道停止条件的情况。使用时需要特别注意避免无限循环。

基本语法

while (条件) {
  # 需要重复执行的代码
  # 记得在循环体内修改条件涉及的变量
}

实用示例

基础计数器:计算从 1 加到 10 的总和。

sum_result <- 0
current <- 1

while (current <= 10) {
  sum_result <- sum_result + current
  current <- current + 1  # 必须更新计数器,否则会无限循环
}

print(sum_result)  # 输出 55

猜数字游戏:让用户猜一个随机数,直到猜对为止。

target_number <- sample(1:100, 1)
guess <- 0
attempts <- 0

cat("猜一个 1 到 100 之间的数字:\n")

while (guess != target_number) {
  guess <- as.integer(readline(prompt = "请输入你的猜测: "))
  attempts <- attempts + 1

  if (guess < target_number) {
    cat("太小了!再试一次。\n")
  } else if (guess > target_number) {
    cat("太大了!再试一次。\n")
  } else {
    cat("恭喜!你猜对了!\n")
    cat("你总共猜了 ", attempts, " 次。\n", sep = "")
  }
}

模拟收敛过程:计算平方根的迭代近似值。

# 计算 25 的平方根,使用牛顿迭代法的简化版本
target <- 25
guess <- 20  # 初始猜测
tolerance <- 0.001
iterations <- 0

while (abs(guess^2 - target) > tolerance) {
  guess <- (guess + target / guess) / 2
  iterations <- iterations + 1
  cat("第", iterations, "次迭代: 猜测值 =", round(guess, 6), "\n")
}

cat("\n最终结果:", round(guess, 6), "\n")
cat("实际平方根:", sqrt(target), "\n")

repeat 循环:灵活但需谨慎

repeat 循环是一种更灵活的循环结构,它会无限重复执行代码块,直到你使用 break 语句明确退出。这种循环特别适用于你不确定循环次数,但知道具体退出条件的场景。

基本语法

repeat {
  # 需要重复执行的代码
  # 必须在适当位置使用 break 退出循环
}

实用示例

安全输入验证:确保用户输入有效数据。

repeat {
  age_input <- readline(prompt = "请输入你的年龄 (1-120): ")
  age <- as.integer(age_input)

  if (!is.na(age) && age >= 1 && age <= 120) {
    cat("有效的年龄输入: ", age, "\n")
    break  # 输入有效,退出循环
  } else {
    cat("输入无效,请重新输入。\n")
  }
}

计算阶乘直到结果超过阈值

n <- 1
factorial_result <- 1

repeat {
  factorial_result <- factorial_result * n
  cat("计算 ", n, "! = ", factorial_result, "\n", sep = "")

  if (factorial_result > 1000000) {
    cat("阶乘结果已超过 100 万,停止计算。\n")
    break
  }

  n <- n + 1
}

寻找满足条件的最小整数

find_target <- 100
current <- 1

repeat {
  if (current^2 > find_target) {
    cat("最小的整数 n,使得 n² > ", find_target, " 是 ", current, "\n", sep = "")
    break
  }
  current <- current + 1
}

循环控制语句:break 与 next

在所有循环结构中,你可以使用 breaknext 来更精确地控制程序流程。

break 语句:立即退出循环

break 语句会立即终止当前循环,继续执行循环之后的代码。

# 找出第一个能被 7 整除的数
numbers <- c(1, 3, 5, 7, 9, 11, 14, 16)

for (num in numbers) {
  if (num %% 7 == 0) {
    cat("找到第一个能被 7 整除的数: ", num, "\n")
    break  # 找到后立即退出循环
  }
  cat("检查 ", num, ",不是 7 的倍数\n", sep = "")
}

next 语句:跳过当前迭代

next 语句会跳过当前迭代的剩余代码,直接进入下一次迭代。

# 打印 1 到 10 之间的所有偶数
for (i in 1:10) {
  if (i %% 2 == 1) {
    next  # 跳过奇数
  }
  cat("偶数: ", i, "\n")
}

结合使用 break 和 next

# 处理数据,跳过无效值,遇到特定条件终止
data_values <- c(5, 10, -1, 20, 30, -5, 40, 50)
threshold <- 100

cat("开始处理数据...\n")

for (value in data_values) {
  if (value < 0) {
    cat("遇到无效值 ", value, ",跳过该值。\n", sep = "")
    next
  }

  current_sum <- sum(data_values[data_values > 0 & data_values <= threshold])

  if (current_sum > threshold) {
    cat("累计和已超过阈值 ", threshold, ",停止处理。\n", sep = "")
    break
  }

  cat("处理值: ", value, ",当前累计和: ", current_sum, "\n", sep = "")
}

循环对比与选择指南

下表总结了三种循环的特点,帮助你在不同场景下做出正确选择:

循环类型 适用场景 终止条件 使用建议
for 已知迭代次数,遍历向量、列表或数据框的每一行/列 自动完成所有迭代 优先选择,代码清晰可读
while 不知道循环次数,但知道继续执行的条件 条件变为 FALSE 适合需要持续检查的场景
repeat 不知道循环次数,需要手动控制退出 遇到 break 语句 适合需要从多个角度判断退出的场景

选择建议

优先使用 for 循环:当你需要遍历一个已知的集合(如数据框的每一行、列表的每个元素、向量中的每个值)时,for 循环是最自然的选择,代码可读性最好。

选择 while 循环:当你需要持续执行某操作直到外部条件变化(如用户输入正确、文件读取完成、网络连接建立)时,while 循环更为合适。

选择 repeat 循环:当你需要多次尝试且退出条件比较复杂(可能在循环体多个位置判断)时,repeat 循环配合 break 使用最为灵活。


性能与最佳实践

向量化优先原则

R 语言是基于向量化的语言,对向量和矩阵的操作有高度优化的内部实现。在可能的情况下,优先使用向量化操作而非循环。

# 低效:使用循环计算向量平方
numbers <- 1:10000
result_loop <- numeric(length(numbers))

for (i in 1:length(numbers)) {
  result_loop[i] <- numbers[i]^2
}

# 高效:使用向量化操作
result_vectorized <- numbers^2

# 两者结果相同,但向量化版本速度快得多
identical(result_loop, result_vectorized)

预分配空间

如果必须使用循环,尽量预先分配结果向量的空间,避免在循环中动态增长向量。

# 不推荐:动态增长向量(效率低)
result <- c()
for (i in 1:1000) {
  result <- c(result, i^2)  # 每次扩展都需要重新分配内存
}

# 推荐:预分配空间
result <- numeric(1000)
for (i in 1:1000) {
  result[i] <- i^2  # 直接赋值,效率高
}

避免全局变量污染

在循环中创建或修改变量时,注意使用局部变量或在适当的环境中操作。

# 使用 local 环境封装循环
calculate_squares <- local({
  result <- numeric(100)
  for (i in 1:100) {
    result[i] <- i^2
  }
  return(result)
})

使用 cat 而非 print 减少输出

在大型循环中,print 会生成大量输出影响性能。如果只是需要查看中间结果,使用 cat 并在最后换行更加高效。

# 对于大型循环,使用 progress bar 监控进度
library(progress)

pb <- progress_bar$new(total = 1000)
for (i in 1:1000) {
  Sys.sleep(0.01)
  pb$tick()
}

实际应用场景

数据清洗与转换

# 批量处理数据框中的缺失值
clean_data <- function(df) {
  for (col_name in names(df)) {
    if (is.numeric(df[[col_name]])) {
      # 用均值替换数值列的缺失值
      df[[col_name]][is.na(df[[col_name]])] <- mean(df[[col_name]], na.rm = TRUE)
    } else if (is.character(df[[col_name]])) {
      # 用 "Unknown" 替换字符列的缺失值
      df[[col_name]][is.na(df[[col_name]])] <- "Unknown"
    }
  }
  return(df)
}

# 示例数据
sample_df <- data.frame(
  age = c(25, NA, 30, NA),
  name = c("Alice", NA, "Bob", "Charlie"),
  score = c(85, 90, NA, 92)
)

cleaned_df <- clean_data(sample_df)
print(cleaned_df)

批量文件处理

# 处理多个 CSV 文件
file_list <- c("data1.csv", "data2.csv", "data3.csv")
combined_data <- data.frame()

for (file_name in file_list) {
  if (file.exists(file_name)) {
    file_data <- read.csv(file_name)
    file_data$source_file <- file_name  # 标记数据来源
    combined_data <- rbind(combined_data, file_data)
    cat("成功处理文件:", file_name, "\n")
  } else {
    cat("警告: 文件", file_name, "不存在\n")
  }
}

蒙特卡洛模拟

# 使用循环进行多次模拟
simulate_pi <- function(n_points) {
  count <- 0
  for (i in 1:n_points) {
    x <- runif(1, -1, 1)
    y <- runif(1, -1, 1)
    if (x^2 + y^2 <= 1) {
      count <- count + 1
    }
  }
  return(4 * count / n_points)
}

# 运行多次模拟并计算平均值
n_simulations <- 10
estimates <- numeric(n_simulations)

for (i in 1:n_simulations) {
  estimates[i] <- simulate_pi(10000)
  cat("第", i, "次模拟结果:", estimates[i], "\n")
}

cat("\n圆周率估计值的均值:", mean(estimates), "\n")
cat("标准差:", sd(estimates), "\n")

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文