Swift 函数完全指南(二):泛型函数与可变参数、函数重载、递归、以及函数式编程思想

泛型函数:让代码从"具体类型"升维到"抽象类型"

  1. 场景:写了一个交换两个 Int 的函数,后来又要交换 Double、String、CGPoint......
  2. 泛型版本:
swift 复制代码
/// 交换任意两个相同类型的值
/// T 是占位符,调用时由编译器推断
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}

var x = 3.14, y = 2.71
swapTwoValues(&x, &y)        // Double
print(x,y)
var s1 = "A", s2 = "B"
swapTwoValues(&s1, &s2)      // String
print(s1,s2)
  1. 泛型约束:只支持"可比较"类型
swift 复制代码
/// 返回数组中最大元素,数组为空时返回 nil
/// T 必须实现 Comparable 协议
func maxElement<T: Comparable>(in array: [T]) -> T? {
    guard var max = array.first else { return nil }
    for e in array.dropFirst() where e > max { max = e }
    return max
}

print(maxElement(in: [3, 1, 4, 2]))         // Int?
print(maxElement(in: ["apple", "zebra"]))   // String?
  1. 多占位符 + 多个约束
swift 复制代码
/// 把字典的 value 映射成新类型,key 不变
func mapValues<K, V, U>(
    _ dict: [K: V],
    _ transform: (V) throws -> U
) rethrows -> [K: U] {
    try dict.mapValues(transform)   // 直接复用标准库
}

易错点:

  • 泛型函数仍然遵循"单态化"(monomorphization) 编译模型,不会带来运行时开销。
  • 如果约束过多,考虑使用 where 子句可读性更好。

函数重载:同名不同参,编译器如何选?

  1. 重载维度
维度 能否作为重载依据
参数个数
参数类型
参数标签
返回类型 ❌(不能单独作为依据)
  1. 示例:看似相同,实则都能共存
swift 复制代码
/// 1. 只有个数不同
func f(_ x: Int) { }
func f(_ x: Int, _ y: Int) { }

/// 2. 类型不同
func f(_ x: String) { }

/// 3. 标签不同
func f(value x: Int) { }
  1. 歧义爆发点:默认参数 + 可变参
swift 复制代码
func sum(_ nums: Int...) -> Int { nums.reduce(0, +) }
func sum(_ a: Int, _ b: Int = 0) -> Int { a + b }

// 以下调用会编译失败:编译器无法决定用哪个
// sum(1)

解决:

  • 把"可变参版本"改成内部判断空数组;
  • 或者干脆改名,避免重载。
  1. 泛型 vs 重载

泛型是"横向"抽象,重载是"纵向"展开。当两者冲突时,编译器优先选"更具体"的重载版本:

swift 复制代码
func printIt<T>(_ x: T) { print("generic: \(x)") }
func printIt(_ x: Int) { print("Int: \(x)") }

printIt(42)      // 输出 Int: 42
printIt("hi")    // 输出 generic: hi

递归:自己调用自己,如何不爆栈?

  1. 经典例子:阶乘
swift 复制代码
/// 普通递归,深度大时可能栈溢出
func factorial(_ n: Int) -> Int {
    n <= 1 ? 1 : n * factorial(n - 1)
}
  1. 尾递归(Tail Recursion)------让编译器做尾调用优化(TCO)
swift 复制代码
/// 把中间结果放到 accumulator 参数里,递归调用是最后一条指令
func factorialT(_ n: Int, _ acc: Int = 1) -> Int {
    n <= 1 ? acc : factorialT(n - 1, acc * n)
}

注意:

  • Swift 5 之后不保证在 -O 优化下一定做 TCO,仅"尽力而为"。
  • 真正要防爆栈,请用循环或 Sequence 惰性计算。
  1. 实战:递归遍历嵌套文件夹(简化版)
swift 复制代码
import Foundation

/// 返回目录下所有 `.swift` 文件
func swiftFiles(in path: String) -> [String] {
    let fm = FileManager.default
    guard let enumerator = fm.enumerator(atPath: path) else { return [] }
    var result: [String] = []
    while let file = enumerator.nextObject() as? String {
        if file.hasSuffix(".swift") {
            result.append((path as NSString).appendingPathComponent(file))
        }
    }
    return result
}

(递归由 FileManagerenumerator 代劳,无需自写)

函数式思维落地:再封装 map / filter / reduce

  1. reduce 的陷阱:初始值类型
swift 复制代码
let nums = [1, 2, 3]
// 错误:拼接字符串却给 Int 初始值
// let r = nums.reduce(0) { $0 + String($1) } // 编译失败

// 正确
let r = nums.reduce("") { $0 + String($1) } // "123"
print(r)
  1. 封装"管道"算子,让代码像搭积木
swift 复制代码
infix operator |> : AdditionPrecedence
/// 把值通过函数"管道"传递
func |> <T, U>(value: T, function: (T) -> U) -> U {
    function(value)
}

/// 使用
let result = [1, 2, 3]
    |> { $0.map { $0 * 2 } }   // [2, 4, 6]
    |> { $0.filter { $0 > 3 } } // [4, 6]
    |> { $0.reduce(0, +) }      // 10
print(result)
  1. 自定义"链式"集合封装
swift 复制代码
struct Chain<Wrapped> {
    private let value: Wrapped
    init(_ value: Wrapped) { self.value = value }
    
    func map<T>(_ transform: (Wrapped) -> T) -> Chain<T> {
        .init(transform(value))
    }
    
    func filter(_ condition: (Wrapped) -> Bool) -> Chain<Wrapped>? {
        condition(value) ? self : nil
    }
    
    func unwrap() -> Wrapped { value }
}

/// 使用
let ans = Chain([1, 2, 3, 4])
    .map { $0.map { $0 * 10 } }   // [10, 20, 30, 40]
    .filter { !$0.isEmpty }!
    .unwrap()
    .reduce(0, +)                 // 100
print(ans)

常见坑位 Top 3

现象 根治方案
1. 可变参 + 默认参重载 编译歧义 改名或删除一个版本
2. 递归无终止条件 运行时崩溃 guard 提前 return
3. 泛型约束过多 编译耗时飙升 把复杂约束拆成 protocol + extension

结语

函数不仅是"代码片段",更是 Swift 世界里的"乐高积木"。

当你能把"泛型 + 重载 + 递归 + 函数式"自由组合时,就拥有了"用函数生产函数"的元编程能力。

愿我们都能把"函数"玩成"艺术",而不是"语法"。

相关推荐
HarderCoder6 小时前
Swift 函数完全指南(一)——从入门到嵌套
swift
jh_cao15 小时前
(4)SwiftUI 基础(第四篇)
ios·swiftui·swift
progalchemist1 天前
Quick SwiftObjective-C测试框架入门教程
开发语言·其他·objective-c·swift
HarderCoder1 天前
Swift 闭包(Closure)从入门到深入:语法、捕获与实战
swift
HarderCoder1 天前
Swift 集合类型详解(三):自定义集合、持久化结构与 ORM 共舞
swift
HarderCoder1 天前
Swift 集合类型详解(一):Array、Set、Dictionary 全貌与选型思路
swift
HarderCoder1 天前
Swift 集合类型详解(二):自定义 Hashable、值语义与性能陷阱
swift
东坡肘子2 天前
Sora 2:好模型,但未必是好生意 | 肘子的 Swift 周报 #0105
人工智能·swiftui·swift
HarderCoder2 天前
Swift 6 并发深渊:@unchecked Sendable 与“隐式 MainActor”如何合谋杀死你的 App
swiftui·swift