Swift: Swift 返回值与函数类型:多返回值、嵌套函数与类型别名
函数不仅能返回基本类型,还能返回多个值、甚至返回另一个函数。本课带你探索 Swift 函数返回值的各种可能,以及函数作为一等公民的核心概念。
1. 你将学到
- 使用元组返回多个值
- 函数作为参数和返回值
- 使用
typealias为函数类型命名 - 嵌套函数及其作用域规则
- 函数重载的基本用法
2. 一个电商开发者的真实故事
(1) 痛点:不同折扣策略需要重复写多个函数
Charlie 在开发一个促销引擎,不同用户等级享受不同折扣:
SWIFT
func vipDiscount(_ price: Double) -> Double {
return price * 0.8
}
func memberDiscount(_ price: Double) -> Double {
return price * 0.9
}
func normalDiscount(_ price: Double) -> Double {
return price
}
每次新增折扣类型就得写一个新函数,促销规则一变又要改一堆代码。策略切换靠手写 if-else,无法动态配置。
(2) 函数作为类型的解法
Swift 中函数是"一等公民",可以像 Int/String 一样被传递和存储:
SWIFT
// 定义折扣策略类型
typealias DiscountStrategy = (Double) -> Double
// 使用函数字典代替 if-else
let strategies: [String: DiscountStrategy] = [
"vip": { $0 * 0.8 },
"member": { $0 * 0.9 },
"normal": { $0 }
]
// 一行切换策略
func applyDiscount(_ price: Double, level: String) -> Double {
return strategies[level]?(price) ?? price
}
(3) 收益:策略可配置、代码可扩展
| 维度 | 改造前 | 改造后 |
|---|---|---|
| 新增折扣 | 写新函数 + 改 if-else | 添加字典条目 |
| 策略切换 | if-else 硬编码 | 按 key 动态选择 |
| 复用性 | 各策略独立,无法组合 | 函数可组合、可传递 |
| 测试覆盖 | 每策略写测试 | 统一测试框架 |
3. 多返回值与函数重载
(1) 使用元组返回多个值
Swift 的元组让函数可以一次性返回多个值:
graph LR
A[函数返回元组] --> B[元素 1]
A --> C[元素 2]
A --> D[元素 3]
B --> E["命名访问: result.name"]
C --> F["命名访问: result.age"]
D --> G["下标访问: result.0"]
| 方式 | 语法 | 适用场景 |
|---|---|---|
| 匿名元组 | -> (Int, String) |
简单组合,不关心名称 |
| 命名元组 | -> (sum: Int, avg: Double) |
需语义化访问各字段 |
| 可选元组 | -> (Int, String)? |
函数可能失败返回 nil |
▶ 示例:数组统计分析
SWIFT
// ============================================
// 返回多个统计值:使用命名元组
// ============================================
func analyze(_ numbers: [Int]) -> (count: Int, sum: Int, average: Double) {
let count = numbers.count
let sum = numbers.reduce(0, +)
let average = count > 0 ? Double(sum) / Double(count) : 0
return (count, sum, average)
}
let result = analyze([85, 90, 78, 92, 88])
print("总数:\(result.count)")
print("总和:\(result.sum)")
print("平均:\(result.average)")
输出:
TEXT 📖 仅展示总数:5 总和:433 平均:86.6
(2) 函数重载
Swift 支持多个同名函数,只要参数类型或数量不同:
SWIFT
func area(width: Double, height: Double) -> Double {
return width * height
}
func area(radius: Double) -> Double {
return Double.pi * radius * radius
}
print("矩形面积:\(area(width: 10, height: 5))")
print("圆形面积:\(area(radius: 3))")
输出:
TEXT 📖 仅展示矩形面积:50.0 圆形面积:28.274333882308138
4. 函数作为类型
(1) 函数类型语法
每个函数都有特定的类型,由参数类型和返回值类型决定:
| 函数签名 | 函数类型 |
|---|---|
func add(a: Int, b: Int) -> Int |
(Int, Int) -> Int |
func log() -> Void |
() -> Void 或 () -> () |
func greet(_ name: String) |
(String) -> Void |
func double(_ n: Int) -> Int |
(Int) -> Int |
💡 注意: 函数类型只包含参数类型和返回值类型,不包含参数标签。
▶ 示例:函数作为变量和参数
SWIFT
// ============================================
// 函数作为变量传递和参数传递
// ============================================
// 1. 定义两个简单函数
func add(_ a: Int, _ b: Int) -> Int { return a + b }
func multiply(_ a: Int, _ b: Int) -> Int { return a * b }
// 2. 函数类型变量
var operation: (Int, Int) -> Int = add
print("add: \(operation(3, 4))")
// 3. 切换函数
operation = multiply
print("multiply: \(operation(3, 4))")
// 4. 函数作为参数
func execute(_ a: Int, _ b: Int, using op: (Int, Int) -> Int) -> Int {
return op(a, b)
}
print("execute add: \(execute(5, 6, using: add))")
print("execute multiply: \(execute(5, 6, using: multiply))")
输出:
TEXT 📖 仅展示add: 7 multiply: 12 execute add: 11 execute multiply: 30
(2) typealias 为函数类型命名
当函数类型较复杂或在多处使用时,用 typealias 取别名:
SWIFT
// 定义函数类型别名
typealias IntOperation = (Int, Int) -> Int
// 使用别名声明变量和参数
func calculate(_ a: Int, _ b: Int, using op: IntOperation) -> Int {
return op(a, b)
}
let op: IntOperation = { $0 * $0 + $1 * $1 }
print("计算:\(calculate(3, 4, using: op))")
输出:
TEXT 📖 仅展示计算:25
5. 嵌套函数与函数返回函数
(1) 嵌套函数
函数内部可以再定义函数,内层函数只能在外层函数作用域内访问:
SWIFT
// ============================================
// 嵌套函数:在函数内部定义辅助函数
// ============================================
func formatFullName(first: String, last: String) -> String {
// 嵌套辅助函数
func capitalize(_ text: String) -> String {
return text.prefix(1).uppercased() + text.dropFirst().lowercased()
}
return "\(capitalize(first)) \(capitalize(last.uppercased()))"
}
print(formatFullName(first: "alice", last: "JOHNSON"))
输出:
TEXT 📖 仅展示Alice Johnson
(2) 函数返回函数
高阶函数的另一种形式——返回一个函数:
SWIFT
// ============================================
// 函数工厂:根据参数生成不同策略函数
// ============================================
func makeMultiplier(factor: Double) -> (Double) -> Double {
func multiplier(_ value: Double) -> Double {
return value * factor
}
return multiplier
}
let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)
print("double(5) = \(double(5))")
print("triple(5) = \(triple(5))")
输出:
TEXT 📖 仅展示double(5) = 10.0 triple(5) = 15.0
▶ 示例:折扣策略工厂
SWIFT
// ============================================
// 使用嵌套函数和函数返回值实现折扣工厂
// ============================================
typealias DiscountRule = (Double) -> Double
func discountFor(level: String) -> DiscountRule? {
// 嵌套函数定义各等级折扣
func vip(_ price: Double) -> Double { return price * 0.8 }
func member(_ price: Double) -> Double { return price * 0.9 }
func normal(_ price: Double) -> Double { return price }
switch level {
case "vip": return vip
case "member": return member
case "normal": return normal
default: return nil
}
}
if let rule = discountFor(level: "vip") {
print("VIP 折扣后:$\(rule(100))")
}
输出:
TEXT 📖 仅展示VIP 折扣后:$80.0
6. 完整示例:促销引擎
SWIFT
// ============================================
// 完整示例:促销引擎
// 功能:多返回值 + 函数类型 + typealias + 嵌套函数
// ============================================
import Foundation
// 1. 定义函数类型别名
typealias Discount = (Double) -> Double
typealias DiscountResult = (original: Double, final: Double, saved: Double)
// 2. 折扣工厂——返回嵌套函数
func createDiscount(minSpend: Double, rate: Double) -> Discount {
func discount(_ price: Double) -> Double {
return price >= minSpend ? price * (1 - rate) : price
}
return discount
}
// 3. 多返回值——返回折扣结果详情
func applyDiscount(price: Double, rule: Discount) -> DiscountResult {
let final = rule(price)
let saved = price - final
return (price, final, saved)
}
// 4. 函数作为参数——批量处理订单
func processOrders(_ prices: [Double], using rule: Discount) -> [DiscountResult] {
return prices.map { applyDiscount(price: $0, rule: rule) }
}
// 5. 使用促销引擎
let memberRule = createDiscount(minSpend: 50, rate: 0.15)
let orderResults = processOrders([120, 45, 200], using: memberRule)
for result in orderResults {
print("$\(result.original) → $\(result.final)(节省 $\(result.saved))")
}
输出:
TEXT 📖 仅展示$120.0 → $102.0(节省 $18.0) $45.0 → $45.0(节省 $0.0) $200.0 → $170.0(节省 $30.0)
❓ 常见问题
Q 函数类型为什么忽略参数标签?
A 函数类型聚焦于类型匹配——参数和返回值的类型。标签属于外部调用约定,不影响类型兼容性。因此
(a: Int) -> Void 和 (b: Int) -> Void 是同一类型。Q 嵌套函数和闭包有什么区别?
A 嵌套函数是有名字的内层函数,闭包通常是匿名的代码块。嵌套函数可以捕获外层变量,行为上与闭包类似,但有名字、可重复调用。
Q typealias 可以用于复杂的泛型函数类型吗?
A 可以。例如
typealias Transformer<T> = (T) -> T 定义一个泛型函数类型别名。但 Swift 中 typealias 不能直接关联泛型约束,需用其他方式实现。Q 返回元组和返回自定义结构体哪个更好?
A 简单组合用元组(如返回两个 Int),语义复杂的场景用结构体(有名字段有名称和文档)。元组适合临时组合,结构体适合公共 API。
Q 函数重载会导致运行时性能下降吗?
A 不会。函数重载在编译时就确定了调用哪个版本(静态派发),没有运行时开销。Swift 编译器根据参数类型和数量在编译期做选择。
📖 小节
- 使用命名元组让函数一次性返回多个有语义的值
- 函数是一等公民,可以赋值给变量、作为参数传递、作为返回值
typealias为函数类型命名,提高代码可读性和复用性- 嵌套函数有外层函数作用域内的可见性,适合封装辅助逻辑
- 函数返回函数(函数工厂)能动态生成不同行为的策略
- 函数重载通过不同的参数类型和数量实现同名函数的多版本
📝 作业
- 基础题: 写一个
divide(_:_:)函数,返回商和余数(使用元组)。调用并打印divide(17, 5)的结果。 - 进阶题: 给
Int数组写一个transform函数,接收一个(Int) -> Int类型的参数,返回新数组。然后使用typealias定义该函数类型的别名。 - 挑战题: 实现一个"运算链"——写一个函数
chain(_:_:),接收一个初始值Int和一个函数数组[(Int) -> Int],依次将每个函数应用于初始值并返回最终结果。然后创建三个转换函数并组成链。