Swift 函数式编程详解
目录
函数式编程概述
函数式编程是一种编程范式,强调使用函数来处理数据,避免改变状态和可变数据。Swift提供了强大的函数式编程支持。
Array的常见操作
基本操作示例
var arr = [1, 2, 3, 4]
// map - 转换每个元素
var arr2 = arr.map { $0 * 2 } // [2, 4, 6, 8]
// filter - 过滤元素
var arr3 = arr.filter { $0 % 2 == 0 } // [2, 4]
// reduce - 聚合操作
var arr4 = arr.reduce(0) { $0 + $1 } // 10
var arr5 = arr.reduce(0, +) // 10
// 使用具名函数
func double(_ i: Int) -> Int { i * 2 }
print(arr.map(double)) // [2, 4, 6, 8]
复杂操作示例
var arr = [1, 2, 3]
// 创建重复数组
var arr2 = arr.map { Array(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3]]
// flatMap - 展平数组
var arr3 = arr.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3]
// compactMap - 过滤nil值
var arr = ["123", "test", "jack", "-30"]
var arr2 = arr.map { Int($0) } // [Optional(123), nil, nil, Optional(-30)]
var arr3 = arr.compactMap { Int($0) } // [123, -30]
高阶函数
map - 转换函数
let numbers = [1, 2, 3, 4, 5]
// 基本用法
let doubled = numbers.map { $0 * 2 }
// [2, 4, 6, 8, 10]
// 转换为字符串
let strings = numbers.map { "Number: \($0)" }
// ["Number: 1", "Number: 2", "Number: 3", "Number: 4", "Number: 5"]
// 转换为其他类型
let floats = numbers.map { Float($0) }
// [1.0, 2.0, 3.0, 4.0, 5.0]
filter - 过滤函数
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 过滤偶数
let evens = numbers.filter { $0 % 2 == 0 }
// [2, 4, 6, 8, 10]
// 过滤大于5的数
let greaterThanFive = numbers.filter { $0 > 5 }
// [6, 7, 8, 9, 10]
// 组合条件
let evenAndGreaterThanFive = numbers.filter { $0 % 2 == 0 && $0 > 5 }
// [6, 8, 10]
reduce - 聚合函数
let numbers = [1, 2, 3, 4, 5]
// 求和
let sum = numbers.reduce(0) { $0 + $1 }
// 15
// 求积
let product = numbers.reduce(1) { $0 * $1 }
// 120
// 连接字符串
let strings = ["Hello", "World", "Swift"]
let joined = strings.reduce("") { $0 + $1 + " " }
// "Hello World Swift "
// 找最大值
let max = numbers.reduce(0) { Swift.max($0, $1) }
// 5
使用reduce实现map和filter
let numbers = [1, 2, 3, 4]
// 使用reduce实现map
let doubled = numbers.reduce([]) { $0 + [$1 * 2] }
// [2, 4, 6, 8]
// 使用reduce实现filter
let evens = numbers.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 }
// [2, 4]
惰性求值
lazy的优化
let numbers = [1, 2, 3, 4, 5]
// 普通操作 - 立即执行
let result1 = numbers.map { $0 * 2 }.filter { $0 > 5 }
// 惰性操作 - 延迟执行
let result2 = numbers.lazy.map { $0 * 2 }.filter { $0 > 5 }
// 只有在需要结果时才执行
let array = Array(result2)
惰性求值的优势
// 处理大数据时的优化
let largeNumbers = Array(1...1000000)
// 惰性求值避免创建中间数组
let result = largeNumbers.lazy
.map { $0 * 2 }
.filter { $0 % 3 == 0 }
.prefix(10) // 只取前10个
print(Array(result))
函数组合
函数组合基础
// 定义基本函数
func add(_ x: Int) -> (Int) -> Int {
return { y in x + y }
}
func multiply(_ x: Int) -> (Int) -> Int {
return { y in x * y }
}
// 组合函数
func compose<A, B, C>(_ f: @escaping (B) -> C, _ g: @escaping (A) -> B) -> (A) -> C {
return { x in f(g(x)) }
}
// 使用组合
let addTwo = add(2)
let multiplyByThree = multiply(3)
let addTwoThenMultiplyByThree = compose(multiplyByThree, addTwo)
let result = addTwoThenMultiplyByThree(5) // (5 + 2) * 3 = 21
管道操作符
// 自定义管道操作符
infix operator |>: AdditionPrecedence
func |><A, B>(value: A, function: (A) -> B) -> B {
return function(value)
}
// 使用管道操作符
let result = 5
|> add(2)
|> multiply(3)
|> { $0 + 1 }
// 22
柯里化
柯里化基础
// 普通函数
func add(_ x: Int, _ y: Int) -> Int {
return x + y
}
// 柯里化函数
func curriedAdd(_ x: Int) -> (Int) -> Int {
return { y in x + y }
}
// 使用柯里化
let addFive = curriedAdd(5)
let result = addFive(3) // 8
// 多参数柯里化
func curriedMultiply(_ x: Int) -> (Int) -> (Int) -> Int {
return { y in { z in x * y * z } }
}
let multiplyByTwoAndThree = curriedMultiply(2)(3)
let result2 = multiplyByTwoAndThree(4) // 24
柯里化的应用
// 配置函数
func configure<T>(_ object: T) -> (@escaping (T) -> Void) -> T {
return { config in
config(object)
return object
}
}
// 使用配置函数
let label = configure(UILabel()) { label in
label.text = "Hello"
label.textColor = .black
label.font = .systemFont(ofSize: 16)
}
函数式编程模式
Optional的函数式操作
let optionalNumber: Int? = 5
// map
let doubled = optionalNumber.map { $0 * 2 } // Optional(10)
// flatMap
let optionalString: String? = "123"
let parsed = optionalString.flatMap { Int($0) } // Optional(123)
// filter
let filtered = optionalNumber.filter { $0 > 3 } // Optional(5)
Result类型的函数式操作
enum NetworkError: Error {
case invalidURL
case noData
}
func fetchData() -> Result<String, NetworkError> {
return .success("Data")
}
// 链式操作
let result = fetchData()
.map { $0.uppercased() }
.flatMap { data in
data.count > 0 ? .success(data) : .failure(.noData)
}
函数式错误处理
func validate(email: String) -> Result<String, ValidationError> {
if email.contains("@") {
return .success(email)
} else {
return .failure(.invalidEmail)
}
}
func sendEmail(to email: String) -> Result<String, NetworkError> {
// 发送邮件逻辑
return .success("Email sent")
}
// 组合操作
let result = validate(email: "test@example.com")
.flatMap { email in sendEmail(to: email) }
实际应用
数据处理管道
struct User {
let name: String
let age: Int
let city: String
}
let users = [
User(name: "Alice", age: 25, city: "Beijing"),
User(name: "Bob", age: 30, city: "Shanghai"),
User(name: "Charlie", age: 35, city: "Beijing")
]
// 函数式数据处理
let beijingAdults = users
.filter { $0.city == "Beijing" }
.filter { $0.age >= 25 }
.map { $0.name }
.sorted()
异步操作的函数式处理
func fetchUserData(userId: String) -> Future<User, Error> {
// 异步获取用户数据
}
func fetchUserPosts(user: User) -> Future<[Post], Error> {
// 异步获取用户帖子
}
// 组合异步操作
let userPosts = fetchUserData(userId: "123")
.flatMap { user in fetchUserPosts(user: user) }
.map { posts in posts.filter { $0.isPublished } }
最佳实践
1. 保持函数纯净
// 纯函数 - 无副作用
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// 避免副作用
func processNumbers(_ numbers: [Int]) -> [Int] {
return numbers.map { $0 * 2 } // 不修改原数组
}
2. 使用不可变数据
// 优先使用let
let numbers = [1, 2, 3, 4, 5]
let processed = numbers.map { $0 * 2 }
// 避免可变状态
struct Counter {
let value: Int
func increment() -> Counter {
return Counter(value: value + 1)
}
}
3. 组合小函数
// 小的纯函数
func isEven(_ number: Int) -> Bool {
return number % 2 == 0
}
func square(_ number: Int) -> Int {
return number * number
}
// 组合使用
let result = numbers
.filter(isEven)
.map(square)
4. 使用函数式错误处理
enum ValidationError: Error {
case empty
case tooShort
case tooLong
}
func validateUsername(_ username: String) -> Result<String, ValidationError> {
if username.isEmpty {
return .failure(.empty)
} else if username.count < 3 {
return .failure(.tooShort)
} else if username.count > 20 {
return .failure(.tooLong)
} else {
return .success(username)
}
}
性能考虑
1. 使用lazy避免不必要的计算
let numbers = Array(1...1000000)
// 高效的惰性求值
let result = numbers.lazy
.map { $0 * 2 }
.filter { $0 > 1000 }
.prefix(10)
2. 避免过度链式调用
// 可能效率较低
let result = numbers
.map { $0 * 2 }
.map { $0 + 1 }
.map { $0 * 3 }
// 更高效的方式
let result = numbers.map { ($0 * 2 + 1) * 3 }
总结
函数式编程为Swift带来了强大的数据处理能力,通过高阶函数、惰性求值、函数组合等特性,可以写出更清晰、更安全的代码。
关键点回顾
- 掌握map、filter、reduce等高阶函数
- 理解惰性求值的优势
- 学会函数组合和柯里化
- 遵循函数式编程的最佳实践
函数式编程的优势
- 代码更简洁易读
- 减少bug的可能性
- 便于测试和调试
- 支持并发编程
注意:函数式编程是一种思维方式,需要时间来适应。建议从简单的高阶函数开始,逐步掌握更复杂的函数式编程模式。