Swift柯里化
Posted WoodBear009
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift柯里化相关的知识,希望对你有一定的参考价值。
Curry
今天同事推荐了一个swift柯里化相关的库,点击打开链接,打开看了看具体实现,瞬间不明觉厉,于是想好好研究研究他是怎么写的,顺便加强一下对柯里化的理解
public func curry<A, B>(_ function: @escaping (A) -> B) -> (A) -> B
return (a: A) -> B in function(a)
public func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C
return (a: A) -> (B) -> C in (b: B) -> C in function(a, b)
public func curry<A, B, C, D>(_ function: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D
return (a: A) -> (B) -> (C) -> D in (b: B) -> (C) -> D in (c: C) -> D in function(a, b, c)
public func curry<A, B, C, D, E>(_ function: @escaping (A, B, C, D) -> E) -> (A) -> (B) -> (C) -> (D) -> E
return (a: A) -> (B) -> (C) -> (D) -> E in (b: B) -> (C) -> (D) -> E in (c: C) -> (D) -> E in (d: D) -> E in function(a, b, c, d)
public func curry<A, B, C, D, E, F>(_ function: @escaping (A, B, C, D, E) -> F) -> (A) -> (B) -> (C) -> (D) -> (E) -> F
return (a: A) -> (B) -> (C) -> (D) -> (E) -> F in (b: B) -> (C) -> (D) -> (E) -> F in (c: C) -> (D) -> (E) -> F in (d: D) -> (E) -> F in (e: E) -> F in function(a, b, c, d, e)
public func curry<A, B, C, D, E, F, G>(_ function: @escaping (A, B, C, D, E, F) -> G) -> (A) -> (B) -> (C) -> (D) -> (E) -> (F) -> G
return (a: A) -> (B) -> (C) -> (D) -> (E) -> (F) -> G in (b: B) -> (C) -> (D) -> (E) -> (F) -> G in (c: C) -> (D) -> (E) -> (F) -> G in (d: D) -> (E) -> (F) -> G in (e: E) -> (F) -> G in (f: F) -> G in function(a, b, c, d, e, f)
public func curry<A, B, C, D, E, F, G, H>(_ function: @escaping (A, B, C, D, E, F, G) -> H) -> (A) -> (B) -> (C) -> (D) -> (E) -> (F) -> (G) -> H
return (a: A) -> (B) -> (C) -> (D) -> (E) -> (F) -> (G) -> H in (b: B) -> (C) -> (D) -> (E) -> (F) -> (G) -> H in (c: C) -> (D) -> (E) -> (F) -> (G) -> H in (d: D) -> (E) -> (F) -> (G) -> H in (e: E) -> (F) -> (G) -> H in (f: F) -> (G) -> H in (g: G) -> H in function(a, b, c, d, e, f, g)
public func curry<A, B, C, D, E, F, G, H, I>(_ function: @escaping (A, B, C, D, E, F, G, H) -> I) -> (A) -> (B) -> (C) -> (D) -> (E) -> (F) -> (G) -> (H) -> I
return (a: A) -> (B) -> (C) -> (D) -> (E) -> (F) -> (G) -> (H) -> I in (b: B) -> (C) -> (D) -> (E) -> (F) -> (G) -> (H) -> I in (c: C) -> (D) -> (E) -> (F) -> (G) -> (H) -> I in (d: D) -> (E) -> (F) -> (G) -> (H) -> I in (e: E) -> (F) -> (G) -> (H) -> I in (f: F) -> (G) -> (H) -> I in (g: G) -> (H) -> I in (h: H) -> I in function(a, b, c, d, e, f, g, h)
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
Introduction to Function Currying in Swift
在它的readme里找到了这篇博客,看后豁然开朗,简单翻译一下(建议看原文)
原文:点击打开链接
柯里化是指把一个多参数的函数变换成一个单参数的新函数,这个新函数接收余下的参数而且返回结果。经过不断的拆解,一个多参数的函数可以被转化成一系列单参数函数,具体的拆解粒度可以根据具体使用情况而定。
柯里化的应用
先来看看下面这个函数,两个整型参数a、b,然后把它们相加
func add(a: Int, b: Int) -> Int
return a + b
我们可以按如下方式调用:
let sum = add(2, 3) // sum = 5
但是设想下面这种情况,如果我们想对一列数字进行加2的处理,得到一个新的序列。借助Range,并对它进行map转换,如下
let xs = 1...100
let x = xs.map add($0, 2) // x = [3, 4, 5, 6, etc]
看起来不是很糟,但是每次都要传入一个默认参数2,总是会让人不爽。而且当我们的处理函数更加复杂时(有多个参数),我们可能就需要传入更多的默认参数。这种类似情况下,便可以借助柯里化帮助我们。
回顾一下我们的add函数定义,传入两个int参数,并返回一个int结果。但是看看map函数的定义,形式如下:
extension Range<A>
func map<B>(transform: A -> B) -> [B]
上面这个扩展实际是无法编译的,这里仅仅是为了更清楚的说明问题
注:实际的系统定义是这样的
public func map<T>(_ transform: (Bound) throws -> T) rethrows -> [T]
可以看到,实际上map函数接收一个参数(transform,参数类型是一个函数,函数传入一个A,返回一个B),并返回一个值([B]).所以我们在调用时可以不使用闭包,而是直接传入一个函数。利用这个特点,我们可以给add函数定义一个包装函数,并直接传给map,如下:
func addTwo(a: Int) -> Int
return add(a, 2)
let xs = 1...100
let x = xs.map(addTwo) // x = [3, 4, 5, 6, etc]
看起来不错,不过这种实现太过有针对性了,如果我们有加3或加100的需求。按照上面这种方式,我们需要针对每个需求都定义一个相应的包装函数,更好的方式应该是做一个更为通用性的支持,比如让我们的函数返回一个函数。修改add的定义:
func add(a: Int) -> (Int -> Int)
return b in a + b
现在我们的函数参数由两个变成了一个。而它的返回值变成了一个函数,这个函数有一个参数,作用是实现a+b,a是add的参数,而b则是返回函数的参数。这种实现意味着:
1.如果我们想调用该函数,并提供所有参数,立刻得到结果,我们的调用形式变成了这样
let sum = add(2)(3) // sum = 5
2.这允许我们"分步"调用add函数.当我们传入第一个参数后,会得到一个新的函数,这个函数等待接收剩下的第二个参数,直到第二个参数被提供时,我们将得到最终的结果。
这样有什么好处吗?
它使我们可以将一个多参数的大函数拆解成一个一个的小函数,以减少每次调用时的参数要求。以我们的addTwo函数为例:
let addTwo = add(2)
addTwo是一个接收一个int参数,返回一个int值的函数变量(let), 我们可以直接把它传给map
let addTwo = add(2)
let xs = 1...100
let x = xs.map(addTwo) // x = [3, 4, 5, 6, etc]
(后面一段关于swift语法层面上对柯里化的支持就不翻译了,因为swift3已经取消了对柯里化的支持)
包装柯里化函数的套路
如果我们给NSNumber定义一个叫做add的扩展方法,如下
extension NSNumber
class func add(a: NSNumber, b: NSNumber) -> NSNumber
return NSNumber(integer: a.integerValue + b.integerValue)
现在,如果我们决定对这个方法进行柯里化
首先,我们需要定义一个函数,参数是一个闭包,闭包的定义遵循NSNumber.add方法,即(NSNumber, NSNumber) -> NSNumber.如下
func curry(localAdd: (NSNumber, NSNumber) -> NSNumber)
然后为这个函数定义返回值,这个返回值是一个闭包,闭包接收一个NSNumber参数,再返回一个闭包,返回的闭包接收一个NSNumber,返回一个NSNumber,(NSNumber -> (NSNumber -> NSNumber))
func curry(localAdd: (NSNumber, NSNumber) -> NSNumber) -> (NSNumber -> (NSNumber -> NSNumber))
其实就是传入一个两个参数的函数localAdd,返回一个只有一个参数的函数,这个函数返回的函数比localAdd少了一个参数,这样就相当于剥离了一个参数。
最后是函数的具体实现
func curry(localAdd: (NSNumber, NSNumber) -> NSNumber) -> (NSNumber -> (NSNumber -> NSNumber))
return a: NSNumber in
b: NSNumber in
return localAdd(a, b) // returns an NSNumber
调用
let curriedAdd = curry(NSNumber.add)
let addTwo = curriedAdd(2)
let xs = 1...100
let x = xs.map(addTwo) // [3, 4, 5, 6, etc]
利用swift的范型机制,我们可以使柯里化的包装更为通用,如下
func curry<A, B, C>(f: (A, B) -> C) -> (A -> (B -> C))
return a: A in
b: B in
return f(a, b) // returns C
套路其实都是一样的,再利用一些swift的语法糖精简一下代码,最终可以得到如下定义
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C
return a in b in f(a, b)
调用
let add = curry(+)
let xs = 1...100
let x = xs.map(add(2)) // [3, 4, 5, 6, etc]
以上是关于Swift柯里化的主要内容,如果未能解决你的问题,请参考以下文章