R 语言入门实战|第七章 程序:从“老虎机”项目学透流程控制与代码优化
引言:第七章为何是 “R 编程进阶” 的关键?
在第二部分 “玩扑克牌” 项目中,我们掌握了数据存储(数据框)、索引取子集和环境管理,但这些操作更偏向 “数据静态处理”。而《R 语言入门与实践》第七章 “程序”,正式进入动态流程控制阶段 —— 通过 “老虎机模拟” 项目,教你如何用if
/else
语句判断条件、用 “查找表” 简化冗余代码、用结构化思维拆解复杂任务,最终编写能自动执行逻辑的完整程序。
本章的核心目标是:理解 “程序 = 有序步骤 + 同类情况” 的设计思路,掌握 R 中流程控制的核心工具,写出逻辑清晰、可复用的程序(如老虎机的play()
函数),为后续循环、面向对象编程打下基础。
一、程序设计的核心策略:拆解任务是关键
第七章开篇就强调:复杂程序的本质是 “简单任务的组合”。面对 “老虎机计算奖金” 这样的复杂需求,需先将任务拆解为两类子任务(文档第七章 7.1 节):
- 有序步骤:按顺序执行的操作(如 “生成 3 个符号→计算奖金→显示结果”);
- 同类情况:需分支判断的场景(如 “3 个相同符号→奖金 80 元”“全是杠→奖金 5 元”“有樱桃→奖金 2 元”)。
实战示例:老虎机任务拆解
以老虎机程序为例,完整流程拆解为 3 个有序步骤,其中 “计算奖金” 又包含 3 种同类情况:
二、流程控制工具:从if
到 “查找表”
第七章的核心工具围绕 “如何处理同类情况” 展开,从基础的if
语句到高效的 “查找表”,逐步优化代码逻辑。
2.1 if
语句:单条件判断的基础
if
语句是 R 中最基础的流程控制工具,用于 “当条件为真时执行某操作”,语法和应用完全贴合文档第七章 7.2 节。
语法格式
if (条件) {# 条件为TRUE时执行的代码
}
- 条件:必须是返回单个
TRUE
/FALSE
的逻辑测试(如symbols[1] == symbols[2]
); - 代码块:条件为真时执行的内容,支持多行代码(建议缩进提升可读性)。
实战:判断老虎机是否出现 “3 个相同符号”
# 示例:检查3个符号是否完全相同
symbols <- c("7", "7", "7") # 老虎机生成的符号
if (symbols[1] == symbols[2] && symbols[2] == symbols[3]) {cat("恭喜!3个符号相同!")
}
# 输出:恭喜!3个符号相同!
关键注意点
- 若条件是逻辑向量(如
c(TRUE, FALSE)
),if
仅会判断第一个元素,并警告 “条件长度> 1”; - 可结合
all()
/any()
将向量压缩为单个逻辑值(如all(symbols == "B")
判断是否全是杠)。
2.2 else
与else if
:多条件分支
当需要处理 “条件为假” 或 “多个互斥条件” 时,需用else
和else if
,对应文档第七章 7.3 节。
语法格式
if (条件1) {# 条件1为TRUE时执行
} else if (条件2) {# 条件1为FALSE、条件2为TRUE时执行
} else {# 所有条件为FALSE时执行
}
实战:老虎机多情况奖金判断
结合第七章表 7-1 的奖金规则,判断不同符号组合的奖金:
# 示例:根据符号组合计算基础奖金
symbols <- c("B", "BB", "BBB")
prize <- 0 # 初始化奖金为0if (symbols[1] == symbols[2] && symbols[2] == symbols[3]) {# 情况1:3个相同符号prize <- 10 # 简化示例,实际需匹配符号类型
} else if (all(symbols %in% c("B", "BB", "BBB"))) {# 情况2:全是杠prize <- 5
} else {# 情况3:其他(含樱桃或无奖)cherry_count <- sum(symbols == "C")prize <- c(0, 2, 5)[cherry_count + 1] # 1个樱桃2元,2个5元
}cat("奖金:$", prize, sep = "") # 输出:奖金:$5
2.3 查找表:替代冗余if-else
的高效工具
第七章 7.4 节重点介绍 “查找表”—— 当需要 “值→值” 的映射(如 “符号→奖金”)时,用命名向量替代冗长的if-else
,代码更简洁、易维护。
查找表原理
通过命名向量存储映射关系,向量的 “名称” 是输入条件,“值” 是对应结果,再通过 “名称取子集” 快速匹配。
实战:老虎机奖金查找表
# 步骤1:创建查找表(符号→奖金,对应第七章表7-1)
payouts <- c("DD" = 100, # 3个钻石"7" = 80, # 3个7"BBB" = 40, # 3个BBB"BB" = 25, # 3个BB"B" = 10, # 3个B"C" = 10, # 3个樱桃"0" = 0 # 3个0(无奖)
)# 步骤2:根据符号匹配奖金
symbols <- c("7", "7", "7")
if (symbols[1] == symbols[2] && symbols[2] == symbols[3]) {prize <- payouts[symbols[1]] # 按符号名称取子集
}cat("奖金:$", prize, sep = "") # 输出:奖金:$80
优势对比
方式 | 代码复杂度 | 维护成本 | 适用场景 |
---|---|---|---|
if-else | 高(多条件嵌套) | 高(新增条件需加分支) | 条件逻辑复杂(如含计算) |
查找表 | 低(向量映射) | 低(新增映射仅需加元素) | 简单 “值→值” 映射 |
三、第七章核心新函数:参数详解与实战
第七章引入多个新函数,用于字符串处理、结果显示和对象属性管理,以下是关键函数的核心参数与应用(完全贴合文档第七章内容)。
3.1 paste()
:字符串拼接
paste()
是 R 中处理字符串的核心函数,第七章用它将老虎机的 3 个符号合并为单个字符串(如 “7DD”),对应文档 7.5 节的slot_display
函数。
核心参数
参数 | 作用说明 | 类型要求 | 默认值 |
---|---|---|---|
... | 待拼接的对象(可多个,如向量、字符串) | 任意类型(自动转字符) | 无 |
sep | 不同对象之间的分隔符 | 字符串 | " " (空格) |
collapse | 若输入是向量,将向量元素合并为单个字符串的分隔符 | 字符串(NULL 表示不合并) | NULL |
实战示例
# 示例1:拼接多个对象(sep参数)
symbols <- c("B", "C", "DD")
paste("符号组合:", paste(symbols, collapse = ""), sep = "")
# 输出:"符号组合:BCD D"(注意:实际collapse会去掉空格,输出"符号组合:BCD D"应为"符号组合:BCD D"修正为"符号组合:BCDD")# 示例2:合并向量元素(collapse参数)
paste(symbols, collapse = "|") # 输出:"B|C|DD"
3.2 cat()
:控制台输出(无引号)
cat()
用于在控制台显示内容,第七章用它美化老虎机结果(如显示 “7DD\n$80”),区别于print()
(会显示引号和行号),对应文档 7.5 节。
核心参数
参数 | 作用说明 | 类型要求 | 默认值 |
---|---|---|---|
... | 待输出的内容(可多个,如字符串、数值) | 任意类型(自动转字符) | 无 |
sep | 不同内容之间的分隔符 | 字符串 | " " |
file | 输出目标(控制台或文件路径) | 字符串("" 表示控制台) | "" |
newline | 输出结束后是否换行 | 逻辑值(TRUE /FALSE ) | TRUE |
实战示例
# 示例:显示老虎机结果(符号+奖金)
symbols_str <- paste(c("7", "DD", "DD"), collapse = "")
prize <- 160 # 3个7+2个钻石,奖金翻倍后160
cat(symbols_str, "$", prize, sep = "\n") # sep="\n"表示换行
# 输出:
# 7DDD
# $160
3.3 structure()
:为对象添加属性
structure()
用于给 R 对象添加自定义属性(如老虎机结果的 “符号组合” 属性),第七章用它让play()
函数同时返回奖金和符号,对应文档 7.5 节。
核心参数
参数 | 作用说明 | 类型要求 | 默认值 |
---|---|---|---|
.Data | 基础对象(如奖金数值) | 任意 R 对象 | 无 |
... | 要添加的属性(键值对,如symbols = symbols ) | 属性名 = 属性值 | 无 |
实战示例
# 示例:为奖金添加“符号组合”属性
get_symbols <- function() {# 第七章定义的生成符号函数wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")sample(wheel, size = 3, replace = TRUE, prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
}score <- function(symbols) {# 简化的奖金计算if (all(symbols == "7")) return(80)return(0)
}# 用structure添加属性
play <- function() {symbols <- get_symbols()prize <- score(symbols)# 为奖金添加symbols属性structure(prize, symbols = symbols)
}# 调用函数并查看属性
result <- play()
attr(result, "symbols") # 提取属性,输出符号组合
3.4 trunc()
:截取数值整数部分
trunc()
在第七章 7.3 节 “else 语句示例” 中出现,用于提取数值的整数部分(忽略小数),常配合数值处理使用。
核心参数
参数 | 作用说明 | 类型要求 | 默认值 |
---|---|---|---|
x | 待处理的数值向量 | 数值型向量 | 无 |
实战示例
# 示例:提取小数的整数部分
a <- 3.14
trunc(a) # 输出:3# 示例:计算数值的小数部分(第七章用法)
dec <- a - trunc(a)
dec # 输出:0.14
四、综合实战:构建完整老虎机程序
结合第七章所有知识点,编写能直接运行的老虎机程序,包含 “生成符号→计算奖金→美化显示” 全流程。
步骤 1:定义get_symbols()
生成随机符号
get_symbols <- function() {# 老虎机符号池(含概率,对应第七章文档)wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")# 按概率抽样3个符号(有放回)sample(x = wheel,size = 3,replace = TRUE,prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
}
步骤 2:定义score()
计算奖金(含查找表)
score <- function(symbols) {# 1. 创建奖金查找表payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25, "B" = 10, "C" = 10, "0" = 0)# 2. 计算基础奖金prize <- 0# 情况1:3个相同符号if (symbols[1] == symbols[2] && symbols[2] == symbols[3]) {prize <- payouts[symbols[1]]} # 情况2:全是杠else if (all(symbols %in% c("B", "BB", "BBB"))) {prize <- 5} # 情况3:含樱桃else {cherry_count <- sum(symbols == "C")prize <- c(0, 2, 5)[cherry_count + 1] # 0/1/2个樱桃对应0/2/5元}# 3. 钻石(DD)翻倍奖金diamond_count <- sum(symbols == "DD")prize * (2 ^ diamond_count)
}
步骤 3:定义play()
整合功能并美化显示
play <- function() {# 1. 生成符号symbols <- get_symbols()# 2. 计算奖金prize <- score(symbols)# 3. 美化显示(用cat和paste)symbols_str <- paste(symbols, collapse = "")cat(symbols_str, "\n$", prize, sep = "")# 4. 返回带属性的奖金structure(prize, symbols = symbols)
}
步骤 4:运行测试
# 玩一次老虎机
play()
# 可能输出:
# B0C
# $2
五、代码注释:第七章强调的 “可读性保障”
第七章 7.5 节专门讲解代码注释,核心原则是 “让自己和他人能看懂代码逻辑”,常用场景:
- 功能说明:函数或代码块的用途(如
# 计算钻石翻倍后的奖金
); - 复杂逻辑注释:关键步骤的解释(如
# 樱桃数量+1是因为向量索引从1开始
); - TODO 注释:待完善的功能(如
# TODO:添加 jackpot 头奖逻辑
)。
示例(带注释的score()
函数片段):
score <- function(symbols) {# 1. 奖金查找表:符号→基础奖金(对应第七章表7-1)payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25, "B" = 10, "C" = 10, "0" = 0)# 2. 统计特殊符号数量(钻石翻倍、樱桃计数)diamond_count <- sum(symbols == "DD")cherry_count <- sum(symbols == "C")# 3. 分支判断奖金(按优先级:相同符号 > 全杠 > 樱桃)# (优先级说明:相同符号奖金最高,需优先判断)if (all(symbols == symbols[1])) {prize <- payouts[symbols[1]]} else if (all(symbols %in% c("B", "BB", "BBB"))) {prize <- 5} else {prize <- c(0, 2, 5)[cherry_count + 1]}# 4. 钻石翻倍(每多1个钻石,奖金×2)prize * (2 ^ diamond_count)
}
六、第七章核心小结
- 程序设计思维:复杂任务拆解为 “有序步骤 + 同类情况”,老虎机项目是典型案例(生成符号→计算奖金→显示结果);
- 流程控制工具:
if
/else
:处理单条件 / 多条件分支,适合逻辑复杂的场景;- 查找表:用命名向量替代冗余
if-else
,适合 “值→值” 映射(如符号→奖金);
- 核心函数:
paste()
:拼接字符串(collapse
合并向量,sep
分隔对象);cat()
:美化输出(无引号、支持换行);structure()
:添加对象属性(如老虎机结果的符号信息);
- 代码规范:注释是保障可读性的关键,需标注功能、逻辑和待完善点。
下一章(第八章)将学习 “R 的 S3 系统”,教你如何为老虎机结果自定义显示格式,进一步提升程序的灵活性 —— 让print(result)
自动显示 “符号 + 奖金”,而非单纯的数值!