Swift 中 curry 特性的高级应用

swift 发布于 2019年11月14日

我们之前的一篇文章,介绍了 curry 的基本用法,这次我们继续上次的探讨,来继续延伸一下 curry 机制的使用范围。

关于 curry 的基本用法,可以参看我们之前的文章:

神奇的 Currying

curry 作用于系统内置方法

我们前面的文章中,介绍了如何对我们自己定义的方法进行 curry 处理,那么如果系统内置的方法,或者别的第三方库中定义的方法呢。我们又如何使用对他们应用 curry 呢?

我们可以创造一个示例,我们为 NSNumber 定义一个方法 multiple:

extension NSNumber {

class func multiple(left: Int, right:Int) -> Int {

return left * right

}

}

在我们不改变这个方法的定义的情况下,怎样能它支持 curry 特性呢。

我们可以定义一个函数,叫做 curry:

func curry(function: (Int,Int) -> Int) -> (Int -> (Int -> Int)){

}

这一大串的定义看着有点眼花缭乱,那么咱们一一解释吧, 首先 curry 接受的参数类型为 (Int,Int) -> Int。 这个表示的是接受两个 Int 类型的参数,并返回一个 Int 类型。也就是我们在 NSNumber 扩展中定义的 multiple 函数类型。 我们可以将 Number.multiple 作为参数传给这个方法。

再看它的返回值,(Int -> (Int -> Int)) 这个表示的是接受 1 个 Int 类型参数,并返回一个 Int -> Int 类型的返回值。这个返回值嘛,毫无疑问又是一个函数。这个返回的函数是一个接受 1 个 Int 参数并返回一个 Int 类型返回值的函数。

curry 这个函数,会把传给它的函数转换成另一个带有 currying 特性的函数。

我们来看看它是如何实现的吧:

func curry(function: (Int,Int) -> Int) -> (Int -> (Int -> Int)){

return { a in

{ b in
return function(a,b)
}

}

}

return 语句首先返回一个外层的闭包:

{ a in
//...
}

这个闭包对应的返回值类型 (Int -> (Int -> Int)),然后第一步闭包内又包含了另外一个闭包:

{ b in
return function(a,b)
}

因为我们 curry 函数的返回值类型是 (Int -> (Int -> Int)), 它还会返回另外一个函数,所以才需要下一层闭包,它对应的是 (Int -> (Int -> Int)) 中的后半部分 (Int -> Int) 类型。

然后最内层的闭包中,调用了 function 函数,将参数 a b 传入进去完成了函数的计算。 function 函数其实对应的就是我们定义的 NSNumber.multiple 方法。

我们来看一下调用方法:

let curriedMultiple = curry(NSNumber.multiple)

curriedMultiple(3)(2)

curry 函数将 NSNumber.multiple 方法转换成了 curriedMultiple 函数,这个函数是支持 curry 特性的函数,所以我们就可以用 curriedMultiple(3)(2) 这种形式调用它了。

这个特性比较灵活。

更通用的做法

我们自己定义的 curry 函数可以将 NSNumber 的 multiple 这个方法转换成支持 currying 特性的函数,但这个 curry 函数受限于参数类型,只能对 Int 类型参数的方法进行转换。

我们还可以使用 Swift 的泛型机制,让这个方法的适应性更强:

func curry<A, B, C>(function: (A,B) -> C) -> (A -> (B -> C)){

return { a in

{ b in
return function(a,b)
}

}

}

通过泛型,我们将具体的参数类型变成了类型参数 A,B,C 这种形式。这样我们的这个 curry 方法就可以对任何接受两个参数的方法进行 currying 处理了。

比如定义了这两个方法:

extension NSNumber {

class func multiple(left: Int, right:Int) -> Int {

return left * right

}

class func add(left:Double, right:Double) -> Double {

return left + right

}

}

就可以使用 curry 函数对这样个方法进行操作:

curry(NSNumber.multiple)(3)(3) // 9
curry(NSNumber.add)(3.0)(3.0) // 6.0

通过泛型,让 curry 函数的使用更加通用。

那么还有一个问题,就是参数的个数,如果我们的参数个数是三个或者更多呢。

已经有人为我们解决这个问题了,就是 Curry 第三方库,它的 Guthub 主页:

https://github.com/thoughtbot/Curry

这个库的实现原理就是我们这篇文章所讲的内容,并且它支持了任意参数数量。


如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe