Swift 柯里化
前言
- 由于柯里化在业务层的应用较少,所以从 Swift 3.0 开始移除了柯里化的用法,但是 Swift 的很多底层特性是使用柯里化来表达的。
1、柯里化
1.1 柯里化简介
柯里化(Currying),又称部分求值(Partial Evaluation),是一种函数式编程思想,就是把接受多个参数的函数转换成接收一个单一参数(最初函数的第一个参数)的函数,并且返回一个接受余下参数的新函数技术。
uncurried: 普通函数
// 接收多个参数的函数(与类相关的函数,统称为方法,但是这里就直接说函数了,方便理解)
func add(a: Int, b: Int, c: Int) -> Int { print("\(a) + \(b) + \(c)")
return a + b + c
}
curried: 柯里化函数
// 柯里化函数,Swift 3.0 之前支持这样的语法,可以直接写
func addCur(a: Int)(b: Int)(c: Int) -> Int { println("\(a) + \(b) + \(c)")
return a + b + c
}
1.2 如何定义柯里化函数
定义柯里化函数
func function name (parameters)(parameters) -> return type { statements
}
1.3 柯里化函数实现原理
uncurried: 普通函数
class Currying { // 接收多个参数的函数
func add(a: Int, b: Int, c: Int) -> Int { print("\(a) + \(b) + \(c)")
return a + b + c
}
}
系统自带的柯里化函数
class Currying { func addCur(a: Int)(b: Int)(c: Int) -> Int { print("\(a) + \(b) + \(c)")
return a + b + c
}
}
手动实现柯里化函数
把上面的函数转换为柯里化函数,首先转成接收第一个参数
a
,并且返回接收余下第一个参数b
的新函数(采用闭包).这里为什么能使用参数
a、b、c
?- 利用闭包的值捕获特性,即使这些值作用域不在了,也可以捕获到他们的值。
- 闭包会自动判断捕获的值是值拷贝还是值引用,如果修改了,就是值引用,否则值拷贝。
- 注意只有在闭包中才可以,
a、b、c
都在闭包中。
class Currying { // (a: Int) : 参数
// (b: Int) -> (c: Int) -> Int : 函数返回值(一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,
// 返回值为 Int 类型的函数) // 定义一个接收参数 a,并且返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。
func add(a: Int) -> (b: Int) -> (c: Int) -> Int { // 返回一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
return { (b:Int) -> (c: Int) -> Int in // 返回一个接收余下第一个参数 c,并且返回结果为 Int 类型的函数
return { (c: Int) -> Int in return a + b + c;
}
}
}
}
1.4 如何调用柯里化函数
创建柯里化类的实例
var curryInstance = Currying()
手动实现的柯里化函数调用
var result: Int = curryInstance.add(a: 10)(b: 20)(c: 30)
- 可能很多人都是第一次看这样的调用,感觉有点不可思议。
- 让我们回顾下 OC 创建对象
[[Person alloc] init]
,这种写法应该都见过吧,就是一下发送了两个消息,alloc
返回一个实例,再用实例调用init
初始化,上面也是一样,一下调用多个函数,每次调用都会返回一个函数,然后再次调用这个返回的函数。
手动实现的柯里化函数拆解调用
curryInstance.add(a: 10)
调用一个接收参数a
,并且返回一个接收参数b
的函数,并且这个函数又返回一个接收参数c
,返回值为Int
类型的函数。// functionB: 一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
let functionB = curryInstance.add(a: 10)
functionB(b: 20)
调用一个接收参数b
的函数,并且这个函数又返回一个接收参数c
,返回值为Int
类型的函数。// functionC: 一个接收参数 c,返回值为 Int 类型的函数
let functionC = functionB(b: 20)
functionC(c: 30)
调用一个接收参数c
,返回值为Int
类型的函数。// result: 函数的返回值
var result: Int = functionC(c: 30);
系统的柯里化函数调用
var result: Int = curryInstance.addCur(a: 10)(b: 20)(c: 30)
系统的柯里化函数拆解调用
curryInstance.addCur(a: 10)
调用一个接收参数a
,并且返回一个接收参数b
的函数,并且这个函数又返回一个接收参数c
,返回值为Int
类型的函数。// Swift是强类型语言,这里没有报错,说明调用系统柯里化函数返回的类型和手动的 functionB 类型一致
// functionB: 一个接收参数 b 的函数,并且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
functionB = curryInstance.addCur(a: 10)
functionB(b: 20)
调用一个接收参数b
的函数,并且这个函数又返回一个接收参数c
,返回值为Int
类型的函数。// functionC: 一个接收参数c,返回值为Int类型的函数
functionC = functionB(b: 20)
functionC(c: 30)
调用一个接收参数c
,返回值为Int
类型的函数。// result: 函数的返回值
result = functionC(c: 30) // 打印 60,60,60 说明手动实现的柯里化函数,和系统的一样。
print("\(r), \(res), \(result)")
1.5 柯里化函数使用注意
必须按照参数的定义顺序来调用柯里化函数,否则就会报错。
柯里化函数的函数体只会执行一次,只会在调用完最后一个参数的时候执行柯里化函数体。
以下调用
functionC(c: 30)
才会执行函数体,这个可以自己断点调试。// curried: 柯里化函数
func addCur(a: Int)(b: Int)(c: Int) -> Int { println("\(a) + \(b) + \(c)")
return a + b + c
} // 创建柯里化类的实例
var curryInstance = Currying() // 不会执行柯里化函数体
functionB = curryInstance.addCur(a: 10) // 不会执行柯里化函数体
functionC = functionB(b: 20) // 执行柯里化函数体
result = functionC(c: 30)
1.6 柯里化函数的好处
这里就需要了解函数式编程思想了,柯里化函数就是运用了函数式编程思想,推荐看这篇文章函数式编程初探。
特点
- 1)只用 “表达式” (表达式: 单纯的运算过程,总是有返回值),不用 “语句” (语句: 执行某种操作,没有返回值)。
- 2)不修改值,只返回新值。
好处
- 1)代码简洁。
- 2)提高代码复用性。
- 3)代码管理方便,相互之间不依赖,每个函数都是一个独立的模块,很容易进行单元测试。
- 4)易于 “并发编程”,因为不修改变量的值,都是返回新值。
1.7 柯里化函数的实用性
实用性一:复用性
需求 1:地图类产品,很多界面都有相同的功能模块,比如搜索框。
- 我们可以利用柯里化函数,来组装界面,把界面分成一个个小模块,这样其他界面有相同的模块,直接运用模块代码,去重新组装下就好了。
实用性二:延迟性
柯里化函数代码需要前面的方法调用完成之后,才会来到柯里化函数代码中。
需求 2:阅读类产品,一个界面的显示,依赖于数据,需要加载完数据之后,才能判断界面显示。
- 这时候也可以利用柯里化函数,来组装界面,把各个模块加载数据的方法抽出来,等全部加载完成,再去执行柯里化函数,柯里化函数主要实现界面的组装。
举例说明
// 组合接口
// 为什么要定义接口,为了程序的扩展性,以后只需要在接口中添加对应的组合方法就好了。
protocol CombineUI { func combine(top: () -> ())(bottom: () -> ())()
} // 定义一个界面类,遵守组合接口
class UI: CombineUI { func combine(top: () -> ())(bottom: () -> ())() { // 搭建顶部
top() // 搭建底部
bottom()
}
}
2、Swift 中实例方法的柯里化
- Swift 中实例方法就是一个柯里化函数。
2.1 Swift 中实例方法的柯里化调用
Swift 中实例方法的柯里化调用
示例结构体
struct Example { var internalStr = "" func combine(externalStr: String) {
print(internalStr + " " + externalStr)
}
}
调用实例方法的常用格式
let example = Example(internalStr: "hello") example.combine(externalStr: "word") // hello word
调用实例方法的柯里化格式
let example = Example(internalStr: "hello") Example.combine(example)(externalStr: "word") // hello word
Swift 柯里化的更多相关文章
- Swift # 柯里化函数
前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...
- swift 学习(二)基础知识 (函数,闭包,ARC,柯里化,反射)
函数 func x(a:Int, b:Int) {} func x(a:Int, b:Int) -> Void {} func x(a:Int, b:Int) ->(Int,Int ...
- Swift开发第四篇——柯里化
本篇分为两部分: 一.柯里化的基本使用 二.柯里化的使用场景 一.柯里化的基本使用 柯里化(Currying):也就是把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数并且返回结果 ...
- Swift函数柯里化(Currying)简谈
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...
- Swift 4.0 废弃的柯里化
// 柯里化 // http://www.jianshu.com/p/6eaacadafa1a Swift 2.0 柯里化方法 (废弃) / ...
- JS 函数的柯里化与反柯里化
===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...
- JS - 柯里化
一:what's this? 柯里化: 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.其实,柯里化就是用闭包原理实现函数 ...
- JAVAScript柯里化、部分应用参数终极理解
一.柯里化 在定义柯里化.部分应用参数的概念前,首先必须对闭包有深入的了解和定义,闭包一句话说清楚:函数返回值为函数. 柯里化的定义:将多参函数分解为按步骤接受单个参数的函数,如下代码: var mo ...
- 【转载】JS中bind方法与函数柯里化
原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情 ...
随机推荐
- POJ 2376 Cleaning Shifts【贪心】
POJ 2376 题意: 给出一给大区间和n各小区间,问最少可以用多少小区间覆盖整个大区间. 分析: 贪心法.设t为当前所有已确定区间的最右端,那我们可以每次都取所有可选的小区间(左端点<=t+ ...
- BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3514 题意概括 N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. N ...
- Github+阿超运算
感谢自己寒假能够稍稍做一点努力. Github个人页面<构建之法阅读笔记二可见>: https://github.com/Heartxy8990 申请教程: http://jingyan. ...
- 026 使用大数据对网站基本指标PV案例的分析
案例: 使用电商网站的用户行为日志进行统计分析 一:准备 1.指标 PV:网页流浪量 UV:独立访客数 VV:访客的访问数,session次数 IP:独立的IP数 2.上传测试数据 3.查看第一条记录 ...
- C# 遍历控件 示例
foreach(Control c in tabControl1.TabPages)//这个循环的意思是说,遍历tabControl1中所有的TabPages,TabPages是包含在tabContr ...
- HDU.5628.Clarke and math(狄利克雷卷积 快速幂)
\(Description\) \[g(i)=\sum_{i_1|i}\sum_{i_2|i_1}\sum_{i_3|i_2}\cdots\sum_{i_k|i_{k-1}}f(i_k)\ mod\ ...
- day01-h1字体大小和文本居中
<!doctype html><html > <head> <meta charset="utf-8"> <link rel= ...
- C++ 函数模板基础知识
为什么要引入模板:为了避免代码重复,程序员可以编写脱离数据类型通用模板. 模板的分类:函数模板 + 类模板 注意:模板的声明或定义只能在全局,命名空间或类范围内进行.不能在函数内进行,比如不能在mai ...
- Kaggle Titanic solution 纯规则学习
其实就是把train.csv拿出来看了看,找了找规律,调了调参数而已. 找到如下规律: 1.男的容易死,女的容易活 2.一等舱活,三等舱死 3.老人死,小孩活 4.兄弟姐妹多者死 5.票价高的活 6. ...
- Java中的ReentrantLock和synchronized两种锁定
原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...