介绍
柯里化是大多数现代编程语言中的一项功能。它将具有多个参数的单个函数转换为一系列函数,每个函数都有一个参数。本质上,这可以将函数存储在变量中并创建返回函数的函数。
虽然乍一看这似乎是一个奇怪的概念,但它是一种强大的技术,有时非常有用。在本教程中,我将向您展示如何在 swift 中利用函数柯里化。
1.带类的函数柯里化
在定义我们自己的自定义函数之前,我将首先向您展示一个使用类在 Swift 中进行柯里化的简单示例。
打开 Xcode,创建一个新的 ios 或 OS X Playground,然后添加以下代码:
class Car { var speed = 0 func accelerateBy(factor: Int) -> Int { speed += factor return speed } } let car1 = Car()
我们定义一个具有一个属性和一个实例方法或函数的基本类。我们还创建了一个类的实例, car1. 我们可以使用以下代码调用实例的 accelerateBy(_:)方法:Car
car1.accelerateBy(10)
但是,还有另一种方法可以通过使用函数柯里化来执行此方法。在上面的示例中,您直接调用该方法, car1尽管该方法实际上是在 Car类中定义的。我们编写的方法不是特定于 car1实例的,而是 特定于Car类的。当你调用这个方法时,真正发生的事情是Swift首先进入 Car类,检索accelerateBy(_:)方法,告诉方法正在使用哪个实例,然后执行特定于该实例的更改方法。为了向您展示它是如何工作的,请将以下行添加到您的 Playground:
Car.accelerateBy(car1)
我们在这里所做的是访问类的 accelerateBy(_:)方法 Car并作为参数传递我们希望在哪个实例上执行该函数。在 Playground 的侧边栏中,您可以看到此方法的结果是另一个函数。
这个 (Function)结果所代表的实际上是一个 accelerateBy(_:)特定于 car1实例的新方法。这个返回函数的独特之处在于它专门引用 了car1的 speed属性,而不是你之前定义的泛型方法,它可以引用 speed任何 Car实例的属性。
正如你所期望的,我们可以将新参数传递给这个返回的函数,以便像往常一样执行它。将以下代码行添加到您的游乐场:
Car.accelerateBy(car1)(10)
使用此代码,我们将10 作为参数传递给 unique accelerateBy(_:)方法并获取 car1作为结果返回的当前速度。
恭喜!您只是第一次在 Swift 中利用了函数柯里化。
除了将多个函数柯里化之外 ,您的代码返回的函数 结果也可以存储到变量中。这允许您存储和快速重用accelerateBy(_:)特定于您的 car1实例的方法。将以下行添加到您的游乐场:
let a = Car.accelerateBy(car1) a(10) a(20) a(30)
您可以看到该变量 a现在的行为与任何其他全局定义的函数一样。优点是它特定于特定 Car实例,可以在运行时而不是编译时定义。在您的 Playground 中,您可以看到边栏中显示的预期输出。
最后,我将向您展示如何通过重新实现 Car我们刚刚看到的类的行为从另一个函数返回一个函数。我们将通过定义一个返回另一个函数的函数来做到这一点。在您的代码中,此函数可能如下所示:
func someFunction(input: AnyObject) -> (AnyObject) -> AnyObject { // 做一些事情来返回一个函数 }
我们定义 someFunction(_:)函数,它接受一个 AnyObject参数并返回另一个函数,该函数也接受一个 AnyObject返回 AnyObject结果的参数。这种类型的函数定义起初看起来非常混乱和令人生畏。为了简化它,我们可以利用 typealiasSwift 中的关键字将新名称映射到任何数据类型。
将以下代码添加到您的游乐场:
typealias IntFunction = (Int) -> Int func accelerationForCar(car: Car) -> IntFunction { return Car.accelerateBy(car) } let newA = accelerationForCar(car1) newA(10)
通过使用 typealias将名称映射 IntFunction到 (Int) -> Int数据类型,我们大大简化了 accelerationForCar(_:)函数定义。(Int) -> Int数据类型只是表示一个接受 Int参数并返回值的 函数Int。
这个新函数使用类的内置行为 Car来返回一个 IntFunction对象,然后该对象可以存储在变量中并像我们以前一样使用。
2. 柯里化自定义函数
虽然 Swift 中的内置函数柯里化行为非常有用,但您可能希望创建自己的与类无关的函数。对于本教程的这一部分,我们将首先使用一个函数示例,该函数将另一个数字乘以一个const ant 值。
想象一下,您想创建一个接受单个输入数字并将其乘以 5 的函数。这个简单的函数可能如下所示:
func multiplyBy5(a: Int) -> Int { return a * 5 }
现在假设您需要一个类似的函数,但是您需要将其乘以 10 而不是 5。然后您需要另一个函数乘以 20。虽然您可以创建三个类似的函数并将它们命名为 multiplyBy5、 multiplyBy10和 multiplyBy20,但这可以处理很多更好地使用函数柯里化。
将以下代码片段添加到您的 Playground:
func multiplyBy(a: Int) -> IntFunction { func nestedMultiply(b: Int) -> Int { return a * b } return nestedMultiply } multiplyBy(10)(20) let multiplyBy5 = multiplyBy(5) multiplyBy5(4)
我们定义了一个 multiplyBy(_:)函数,它接受一个Int 作为它的唯一参数并返回一个 type 的函数, IntFunction我们在本教程前面定义的数据类型。在这个函数中,我们定义了另一个函数, nestedMultiply(_:)。我们将这个函数嵌套在第一个函数中,这样它就不能在 multiplyBy(_:)函数范围之外执行,并且可以访问 a输入参数。
函数定义下面的三行是如何将函数 curry 在一起的简单示例。
虽然您可以使用嵌套函数创建用于柯里化的函数,但您也可以使用闭包或通过定义多组参数来创建它们。例如,将以下代码片段添加到您的 Playground:
func add(a: Int) -> IntFunction { return { b in a + b } } let add2 = add(2) add2(4) func subtract(a: Int)(b: Int) -> Int { return b - a } let subtract5 = subtract(5) subtract5(b: 8)
如您所见,这两个新函数可以以与本教程中所有其他函数相同的方式一起柯里化。唯一的例外是,对于使用两组参数定义的函数,您必须显式命名第二个参数。
您可以在代码中实现的函数柯里化级别的数量没有限制。以下代码显示了三个 curried 函数的示例,以及它与具有三个参数的单个函数的不同之处。
func multiply(a: Int, b: Int, c: Int) -> Int { return a * b * c } func multiply(a: Int) -> (Int) -> IntFunction { func nestedMultiply1(b: Int) -> IntFunction { func nestedMultiply2(c: Int) -> Int { return a * b * c } return nestedMultiply2 } return nestedMultiply1 } multiply(4, 5, 6) multiply(4)(5)(6)
这两个乘法函数之间的主要区别在于,在处理完第一个或第二个参数后,柯里化版本可以有效地暂停并存储在变量中。这使得函数很容易重用和作为对象传递。
结论
Swift 中的函数柯里化是一个难以掌握的概念,但其核心基本上是两个关键概念:
创建返回其他函数的函数
将具有多个参数的函数简化为一系列函数,每个函数都有一个参数
有多种方法可以将函数组合在一起,虽然 Int本教程中只使用了数据类型,但相同的过程可以用于 Swift 项目中的任何数据类型。这使得函数可以存储在变量中并在整个代码中多次使用。