25-函数式编程

82 阅读4分钟

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的可能性
  • 便于测试和调试
  • 支持并发编程

注意:函数式编程是一种思维方式,需要时间来适应。建议从简单的高阶函数开始,逐步掌握更复杂的函数式编程模式。