Swift 中的泛型和函数式编程

zvw*_*iss 4 generics functional-programming swift

下面 sum 函数的两个变体是我尝试用 Swift 重复 Abelson 和 Sussman 在经典的《计算机程序的结构和解释》一书中介绍的 lisp 版本。第一个版本用于计算某个范围内的整数之和,或某个范围内的整数的平方和,第二个版本用于计算 pi/8 的近似值。

我无法将这些版本组合成一个可以处理所有类型的函数。有没有一种巧妙的方法来使用泛型或其他一些 Swift 语言功能来组合变体?

func sum(term: (Int) -> Int, a: Int, next: (Int) -> Int, b: Int) -> Int {
    if a > b {
        return 0
    }
    return (term(a) + sum(term, next(a), next, b))
}

func sum(term: (Int) -> Float, a: Int, next: (Int) -> Int, b: Int) -> Float {
    if a > b {
        return 0
    }
    return (term(a) + sum(term, next(a), next, b))
}
Run Code Online (Sandbox Code Playgroud)

sum({$0}, 1, {$0 + 1}, 3)
Run Code Online (Sandbox Code Playgroud)

结果 6

sum({$0 * $0}, 3, {$0 + 1}, 4)
Run Code Online (Sandbox Code Playgroud)

结果 25

8.0 * sum({1.0 / Float(($0 * ($0 + 2)))}, 1, {$0 + 4}, 2500)
Run Code Online (Sandbox Code Playgroud)

结果为 3.14079

Seb*_*ian 5

为了使其更容易一些,我稍微更改了方法签名,并假设它足以适用于func sum_ <T> (term: (T -> T), a: T, next: (T -> T), b: T) -> T {,其中 T 是某种数字。

不幸的是,Swift 中没有Number类型,所以我们需要创建自己的类型。我们的类型需要支持

  • 添加
  • 比较
  • 0(添加中性元素)

满足功能要求的新协议

比较是在Comparable协议中处理的,对于重置,我们可以创建自己的协议:

protocol NeutralAdditionElementProvider {
    class func neutralAdditionElement () -> Self
}

protocol Addable {
    func + (lhs: Self, rhs: Self) -> Self
}
Run Code Online (Sandbox Code Playgroud)

sum执行

我们现在可以实现该sum功能:

func sum <T where T:Addable, T:NeutralAdditionElementProvider, T:Comparable> (term: (T -> T), a: T, next: (T -> T), b: T) -> T {
    if a > b {
        return T.neutralAdditionElement()
    }
    return term(a) + sum(term, next(a), next, b)
}
Run Code Online (Sandbox Code Playgroud)

制定IntDouble遵守协议

+已经实现了DoubleInt因此协议一致性很容易:

extension Double: Addable {}

extension Int: Addable {}
Run Code Online (Sandbox Code Playgroud)

提供中性元素:

extension Int: NeutralAdditionElementProvider {
    static func neutralAdditionElement() -> Int {
        return 0
    }
}

extension Double: NeutralAdditionElementProvider {
    static func neutralAdditionElement() -> Double {
        return 0.0
    }
}
Run Code Online (Sandbox Code Playgroud)