本节摘要:有了名字和值,还要让它们按条件走不同的路。本节讲 if / else if / else 的阶梯、for-in 与范围的三种形态、while 与 repeat-while 的差别,以及 Swift 里被严重低估的 switch——值绑定、元组匹配与 where 从句让它远不止"多路 if"。落点是阳台花园的浇水调度逻辑:同一段代码对喜湿与耐旱品种给出不同建议。
上一节我们把"已过天数"与"间隔"存了下来,但养分不会自己流动——什么时候该浇、什么时候该缓,得靠分岔。先看最朴素的阶梯判断,注意 Swift 的条件表达式不需要括号(写了也不报错,但社区风格是省略):
// 浇水紧迫度分级:三级阶梯 let daysSinceWater = 5 let interval = 3 if daysSinceWater >= interval + 2 { print("严重缺水,立即浇水并检查叶片") } else if daysSinceWater >= interval { print("到日子了,今天浇水") } else { let remaining = interval - daysSinceWater print("还剩 \(remaining) 天,先忍住") } // 控制台输出:严重缺水,立即浇水并检查叶片
阶梯自上而下短路:命中一支就离开,后面的不再看。写阶梯的技巧是把最紧急的条件放最上面——顺序即优先级。
⚠️ 常见坑:Swift 的 if 条件必须是 Bool,不能像某些语言那样写
if daysSinceWater {。数字、非空字符串都不算真假,想判空先显式比较,例如if daysSinceWater != 0。这条限制逼你把"真"写明白。
for-in 配范围是最常用的形态,范围有三种写法,闭开有别:
// 闭范围 1...5 含头含尾;半开 1..<5 含头不含尾;stride 自定义步长 for day in 1...7 { print("第 \(day) 天", terminator: " ") } print() // 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 for pot in 1..<4 { print("花盆 \(pot) 号已检查") } // 只到 3 号 for mark in stride(from: 0, through: 60, by: 15) { // 0 15 30 45 60 print("湿度刻度 \(mark)%") } // 遍历集合才是日常:一排植物挨个点名 let plants = ["龟背竹", "虎皮兰", "绿萝", "多肉"] for (index, name) in plants.enumerated() { print("\(index + 1). \(name)") } // 输出: // 1. 龟背竹 // 2. 虎皮兰 // 3. 绿萝 // 4. 多肉
while 适合"不知道要转几圈"的场景:模拟缺水植物恢复,湿度每天回升,直到达标才停。
var humidity = 0.28 let target = 0.60 var simulateDays = 0 while humidity < target { humidity += 0.12 // 每天补水恢复一成多 simulateDays += 1 } print("恢复到目标湿度需 \(simulateDays) 天,当前 \(humidity)") // 恢复到目标湿度需 3 天,当前 0.64 // repeat-while:先做后判,至少执行一次 var menuChoice = "" repeat { menuChoice = plants.randomElement() ?? "龟背竹" // 随机挑一株今日推荐 } while menuChoice == "多肉" // 多肉今天刚浇过,换一株 print("今日推荐:\(menuChoice)")
Swift 的 switch 不穿透、必须穷尽,还有值绑定与 where 两件武器。阳台花园的"品种调度"用它写最干净:
// 按品种与状态给出浇水建议:元组匹配 + 值绑定 + where func advice(kind: String, days: Int) -> String { switch (kind, days) { case ("多肉", let d) where d < 14: return "多肉耐旱,\(d) 天不用管" case ("多肉", _): return "多肉超过两周了,沿盆边少浇一点" case ("绿萝", let d) where d >= 2: return "绿萝喜湿,已 \(d) 天,快浇" case ("绿萝", _): return "绿萝土壤微润即可" case (_, let d) where d >= 3: return "常规植物,\(d) 天了,浇水" default: return "状态良好,按计划来" } } print(advice(kind: "多肉", days: 5)) // 多肉耐旱,5 天不用管 print(advice(kind: "绿萝", days: 2)) // 绿萝喜湿,已 2 天,快浇 print(advice(kind: "虎皮兰", days: 4)) // 常规植物,4 天了,浇水
_ 是通配符,let d 把匹配到的值绑定进来用,where 给分支加守卫。对比一下,用 if-else 写同样的逻辑要嵌套两层、重复比较品种名——switch 的分支各自独立,加一种品种就是加一个分支,不惊动其他行。
背景:列表页要显示未来一周的浇水计划,不同品种节奏不同。操作:组合循环与分支,生成排程。
let schedule: [String: Int] = ["龟背竹": 3, "绿萝": 2, "多肉": 10] // 字典:品种到周期 for day in 1...7 { var dueToday: [String] = [] for (name, cycle) in schedule where day % cycle == 0 { dueToday.append(name) } if dueToday.isEmpty { print("第 \(day) 天:全体休息") } else { print("第 \(day) 天:浇水 \(dueToday.joined(separator: "、"))") } } // 输出(节选): // 第 1 天:全体休息 // 第 2 天:浇水 绿萝 // 第 3 天:浇水 龟背竹 // 第 6 天:浇水 龟背竹、绿萝
结果:一份随品种配置变化的排程。解读:字典的遍历顺序不保证稳定,输出顺序可能与插入顺序不同——界面要稳定排序就得先排序再遍历;这也是"数据结构特性会渗到界面表现"的第一个例子。变式:把周期改成从植物模型数组里取(2.4 节的 struct),排程逻辑一行不用改——这就是抽象的好处。
... 含尾、..< 不含尾、stride 定步长;分岔与循环解决了"怎么流",下一节解决"从哪流到哪"——函数与闭包这对输送管道。