R: R 控制流:if/for/while 条件判断与循环
最后更新:2026-08-26
上一课我们学了运算符,R 程序能"判断真假"了。但光会判断还不够——程序还要能重复做事(循环)和分支选择(条件)。这一课我们学 R 的"控制流":if/else 条件、for/while 循环、repeat 跳出。
R 的控制流和 Python 不太一样:R 用 {} 包围代码块(不用缩进),并且整个 if/else 是一个表达式。这一课我们要掌握这些差异。
1. 你将学到
- if / else / else if 条件分支
- for 循环遍历向量/列表
- while 条件循环
- repeat 无限循环(用 break 退出)
- ifelse() 向量化判断
- next 跳过本轮、break 跳出循环
- R 与 Python 控制流的关键差异
2. 一个数据筛选的故事
(1) 痛点:手动分类
小赵是数据分析师,manager让他把 100 个客户的年龄做分层:
- 18 岁以下:未成年
- 18-30 岁:青年
- 31-50 岁:中年
- 51 岁以上:老年
如果用 Excel,要写 4 个嵌套 IF 公式;如果用 Python,要写循环和 if-else。但 R 有专门为向量设计的 ifelse()——
(2) R 的解法
# 5 个客户的年龄
ages <- c(15, 22, 35, 50, 65)
# 一行代码完成 4 档分类(向量化 ifelse)
labels <- ifelse(ages < 18, "未成年",
ifelse(ages < 31, "青年",
ifelse(ages < 51, "中年", "老年")))
cat("客户分类:\n")
print(data.frame(年龄 = ages, 类别 = labels))
3 行代码解决 4 档分类。这就是 R 控制流的"向量化"优势——一句话就是一个判断向量。
3. if/else:条件分支
(1) if 基本语法
# R 用 { } 包围代码块(Python 用缩进)
if (条件) {
条件为真时执行
}
age <- 18
if (age >= 18) {
cat("你已经成年了\n")
}
# Output:你已经成年了
(2) if-else:二选一
if (条件) {
条件为真时执行
} else {
条件为假时执行
}
score <- 45
if (score >= 60) {
cat("及格了!\n")
} else {
cat("不及格,需要补考\n")
}
# Output:不及格,需要补考
(3) if-else if-else:多分支
if (条件1) {
分支1
} else if (条件2) {
分支2
} else {
其他
}
score <- 85
if (score >= 90) {
cat("优秀\n")
} else if (score >= 80) {
cat("良好\n")
} else if (score >= 60) {
cat("及格\n")
} else {
cat("不及格\n")
}
# Output:良好
(4) R vs Python 关键差异
| 特性 | R | Python |
|---|---|---|
| 代码块 | {} 大括号 |
缩进(4 空格) |
| if 写法 | if (x > 0) { ... } |
if x > 0: |
| 整体表达式 | if/else 是一个表达式(可赋值) | if/else 是语句(不能赋值) |
| 三元运算 | ifelse() 专门函数 |
x if x > 0 else -x |
# R 的特殊用法:ifelse 是表达式
result <- if (score >= 60) "及格" else "不及格"
result
# [1] "及格"
else 在同一行(如果整体作为表达式)。如果分两行,R 会报语法错。
4. ifelse():向量化判断
(1) 为什么需要 ifelse()?
普通 if 只能判断单个值,但 R 经常需要判断整个向量。这时用 ifelse():
graph LR
A["x = 1, 2, 3, 4, 5"] --> B["ifelse x > 3 yes no"]
B --> C["no no no yes yes"]
style A fill:#cce5ff
style C fill:#d4edda
# 普通 if 会报错(只能接受长度 1)
x <- c(1, 2, 3, 4, 5)
if (x > 3) "大" else "小" # ❌ 报错:condition has length > 1
# 用 ifelse() 解决
ifelse(x > 3, "大", "小")
# [1] "小" "小" "小" "大" "大"
(2) ifelse() 语法
ifelse(条件, 条件为真时的值, 条件为假时的值)
(3) 嵌套 ifelse():多档分类
ages <- c(15, 22, 35, 50, 65)
# 4 档分类
labels <- ifelse(ages < 18, "未成年",
ifelse(ages < 31, "青年",
ifelse(ages < 51, "中年", "老年")))
print(data.frame(年龄 = ages, 类别 = labels))
输出:
年龄 类别
1 15 未成年
2 22 青年
3 35 中年
4 50 中年
5 65 老年
ifelse() 嵌套超过 3 层建议用 dplyr::case_when()(第 10 课学),更清晰。
5. for 循环:遍历元素
(1) for 基本语法
for (变量 in 向量) {
每次循环执行的代码
}
(2) 遍历向量
# 遍历 1:5
for (i in 1:5) {
cat("第", i, "次循环\n")
}
# Output:
# 第 1 次循环
# 第 2 次循环
# 第 3 次循环
# 第 4 次循环
# 第 5 次循环
# 遍历字符向量
fruits <- c("苹果", "香蕉", "樱桃")
for (fruit in fruits) {
cat("我喜欢吃", fruit, "\n")
}
(3) 累计求和(经典例子)
# 1+2+3+...+100
total <- 0
for (i in 1:100) {
total <- total + i
}
total
# [1] 5050
(4) ⚠️ R 的循环性能差
R 的 for 循环比 Python 慢 10-100 倍。R 鼓励用向量化运算代替循环:
# 慢(循环)
system.time({
total <- 0
for (i in 1:1e6) {
total <- total + i
}
})
# 用户 系统 流逝
# 0.40 0.00 0.40 ← 0.4 秒
# 快(向量化)
system.time({
total <- sum(1:1e6)
})
# 用户 系统 流逝
# 0.01 0.00 0.01 ← 0.01 秒(快 40 倍)
sum() mean() ifelse() 解决的,不要写循环。
6. while 循环:条件循环
(1) while 语法
while (条件) {
条件为真时重复执行
}
(2) 经典例子:猜数字游戏
# 目标数字
target <- 42
guess <- 0
attempts <- 0
while (guess != target) {
guess <- sample(1:100, 1) # 随机猜
attempts <- attempts + 1
if (attempts > 1000) break # 超过 1000 次放弃
}
cat("用了", attempts, "次猜中\n")
(3) 与 for 的区别
| 特性 | for | while |
|---|---|---|
| 用途 | 已知次数的循环 | 未知次数,条件触发 |
| 终止 | 遍历完向量结束 | 条件为假结束 |
| 适用 | 遍历数据 | 等待事件、轮询 |
7. repeat 与 break:无限循环
(1) repeat 语法
repeat {
代码
if (条件) break # 必须有 break,否则死循环
}
(2) 实战:菜单选择
choice <- 0
repeat {
cat("\n=== 主菜单 ===\n")
cat("1. 查天气\n")
cat("2. 查股票\n")
cat("0. 退出\n")
choice <- as.integer(readline("请选择:"))
if (choice == 0) {
cat("再见!\n")
break
} else if (choice == 1) {
cat("今天晴,25°C\n")
} else if (choice == 2) {
cat("上证 3000 点\n")
} else {
cat("无效输入\n")
}
}
repeat 必须配合 break,否则会死循环(程序卡住)。
8. next 和 break:循环控制
| 关键字 | 作用 | 场景 |
|---|---|---|
next |
跳过本轮,进入下一轮 | 跳过某些元素 |
break |
跳出整个循环 | 找到目标立即退出 |
# next:跳过偶数
for (i in 1:10) {
if (i %% 2 == 0) next
cat(i, " ")
}
# Output:1 3 5 7 9
cat("\n")
# break:找到第一个 7 就退出
for (i in 1:10) {
if (i == 7) break
cat(i, " ")
}
# Output:1 2 3 4 5 6
9. 常见陷阱
(1) 浮点比较陷阱
# 看似相等,实际不等
0.1 + 0.2 == 0.3
# [1] FALSE ← 实际是 0.30000000000000004
# 正确做法:容差比较
abs(0.1 + 0.2 - 0.3) < 1e-9
# [1] TRUE
(2) else 必须换行/同行
# ❌ 错误:else 单独成行
if (TRUE) {
cat("yes")
}
else {
cat("no")
}
# Error: unexpected 'else' in "else"
# ✅ 正确:else 在 if 块的 } 后面
if (TRUE) {
cat("yes")
} else {
cat("no")
}
# Output:yes
} else { 写在同一行,避免解析歧义。
(3) if 不会自动打印
# R 中 if 不会"自动打印"(不像函数调用)
if (TRUE) "yes"
# 没有输出 ← 不打印
# 解决:用 cat() 或 print() 显式输出
if (TRUE) cat("yes\n")
# Output:yes
10. 完整示例:客户分级管理
下面是一个完整工作流示例,把本课所有控制流串起来。
▶ 示例:客户分级与营销策略
# ============================================
# 客户分级与营销策略
# 功能:根据年龄+消费金额给客户分级
# ============================================
# 1. 准备 6 个客户数据
customers <- data.frame(
name = c("Alice", "Bob", "Charlie", "赵六", "钱七", "孙八"),
age = c(15, 22, 35, 50, 65, 28),
spend = c(100, 500, 2000, 5000, 10000, 800)
)
# 2. 用 ifelse() 批量分级(向量化)
customers$tier <- ifelse(customers$spend < 500, "普通",
ifelse(customers$spend < 3000, "银卡",
ifelse(customers$spend < 8000, "金卡", "钻石")))
# 3. 遍历客户,输出营销策略(for 循环)
cat("=== 客户分级与营销策略 ===\n\n")
for (i in 1:nrow(customers)) {
name <- customers$name[i]
age <- customers$age[i]
spend <- customers$spend[i]
tier <- customers$tier[i]
# 跳过钻石用户(已最优)
if (tier == "钻石") {
cat(name, "(", tier, "):", spend, "元,老客户,重点维护\n")
next
}
# 跳出条件(演示 break)
if (spend > 5000) {
cat(name, ":高消费客户!进入 VIP 流程\n")
}
# 按年龄推荐营销
if (age < 18) {
cat(name, "(", tier, "):未成年,推荐家长卡\n")
} else if (age < 30) {
cat(name, "(", tier, "):青年,推送新品\n")
} else if (age < 50) {
cat(name, "(", tier, "):中年,推送家庭套餐\n")
} else {
cat(name, "(", tier, "):中老年,推送健康产品\n")
}
}
预期输出(节选):
=== 客户分级与营销策略 ===
Alice( 普通 ):未成年,推荐家长卡
Bob( 普通 ):青年,推送新品
Charlie( 银卡 ):中年,推送家庭套餐
赵六( 金卡 ):中年,推送家庭套餐
钱七( 钻石 ):10000 元,老客户,重点维护
孙八( 银卡 ):青年,推送新品
❓ 常见问题
sum() mean() ifelse() 解决的,不要写 for。R 循环比 Python 慢 10-100 倍,10 万元素以上慎用。📖 小节
- if/else/else if 条件分支,R 用
{}包围代码块(不用缩进) - ifelse() 是向量化判断,可对整个向量分类;普通 if 只能判断单个值
- for 循环遍历已知次数;while 条件循环未知次数;repeat 无限循环必须配 break
- next 跳过本轮、break 跳出整个循环
- R 鼓励向量化优先——for 循环比 Python 慢 10-100 倍,大数据用
sum()ifelse()替代 - 浮点比较用容差(
abs(x - y) < 1e-9),不要直接== else必须与}同行(避免解析歧义)
📝 作业
-
基础题:写一个 R 脚本,用
if/else if/else实现:根据分数score输出等级(90+优秀,80+良好,60+及格,否则不及格)。测试 4 个分数:95, 75, 60, 40,把输出截图保存。 -
基础题:用
ifelse()把向量c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)分为奇数("odd")和偶数("even"),输出结果。 -
基础题:用
for循环计算 5!(5 的阶乘 = 5×4×3×2×1),把过程和结果截图保存。 -
进阶题:写一个猜数字游戏:① 用
sample(1:100, 1)生成 1-100 随机数作为目标;② 用while循环让用户输入(readline);③ 提示"太大"或"太小";④ 猜中后输出"恭喜!用了 N 次"。运行测试。 -
挑战题:写一段脚本,对 1-100 的整数做如下处理:① 跳过 3 的倍数(
next);② 遇到 50 时跳出(break);③ 把剩下的数累加求和。验证结果(应该是 1+2+4+5+7+8+...+49 = ?)。把代码和输出截图保存。