Swift 中 curry 特性的高级应用

1,539 阅读4分钟

curry 是 Swift 中一个很灵活的特性,用最简单的语言来说,curry 就是一个用方法生成方法的机制。它能让我们的方法定义更加动态化,甚至对于系统内置的方法,都可以通过 curry 的方法进行整合。

我们之前的一篇文章,介绍了 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 语句首先返回一个外层的闭包:

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

{ a in
	//...
}

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

{ b in
	return function(a,b)
}

然后最内层的闭包中,调用了 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(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 主页:

github.com/thoughtbot/…

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

本站文章均为原创内容,如需转载请注明出处,谢谢。