函数式编程
- 函数式编程:是一种编程范式,也就是如何编写程序的方法论;
- 其主要思想为:把计算过程尽量分解成一系列可复用的函数调用;
- 其主要特征为:函数与其他数据类型一样的地位,可以赋值给其他变量,也可以作函数的参数,函数的返回值;
import Foundation
let num = 1
//第一种实现方法
func add(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int,_ v2: Int) -> Int { v1 - v2}
func multipe(_ v1: Int,_ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int,_ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int,_ v2: Int) -> Int { v1 % v2 }
let reslut = divide(mod(sub(multipe(add(num, 3), 5), 1), 10), 2)
print(reslut)//4
//第二种实现方法
//返回值类型 (Int) -> Int v1+(Int参数)
//add1函数的参数v1,作为内部闭包函数的实现的一部分;
//$0是闭包函数的参数 也就是当前函数的 (返回值函数) 的参数;
func add1(_ v1: Int) -> (Int) -> Int { {$0 + v1} }
func sub1(_ v1: Int) -> (Int) -> Int { {$0 - v1} }
func multipe1(_ v1: Int) -> (Int) -> Int { { $0 * v1 } }
func divide1(_ v1: Int) -> (Int) -> Int { { $0 / v1 } }
func mod1(_ v1: Int) -> (Int) -> Int { { $0 % v1 } }
let fn1 = add1(3)
let fn2 = multipe1(5)
let fn3 = sub1(1)
let fn4 = mod1(10)
let fn5 = divide1(2)
let result1 = fn5(fn4(fn3(fn2(fn1(num)))))
print("result1 = ",result1)//4
//第三种实现方法:
//函数合成
//f1与f2不是在composite内部调用的 是在闭包中调用的 属于逃逸闭包,则需加@escaping
func composite(_ f1: @escaping (Int) -> Int,_ f2: @escaping (Int) -> Int) -> (Int) -> Int {
{
f2(f1($0))
}
}
//(Int) -> Int
let fn6 = composite(add1(3), multipe1(5))(num)
print("fn6 = ",fn6) //20
//自定义运算符
infix operator >>> : AdditionPrecedence
func >>>(_ f1: @escaping (Int) -> Int,_ f2: @escaping (Int) -> Int) -> (Int) -> Int {
{
f2(f1($0))
}
}
let fn7 = add1(3) >>> multipe1(5) >>> sub1(1) >>> mod1(10) >>> divide1(2)
print("result2 = ",fn7(num))//4
import Foundation
let num = 1
///add函数的返回值 是一个 (参数为Int,返回值为Int) 的函数
///其中$0 是add函数的 [返回值函数] 的入参 也就是下面的入参num
func add(_ v1: Int) -> (Int) -> Int { { $0 + v1 } }
let functionAdd = add(3)
let addNum = functionAdd(num)
print(addNum)
高阶函数
- 高阶函数是至少满足下列一个条件的函数:
- 接收一个或者多个函数,作为输入参数;
- 返回一个函数;
- 在函数式编程中,到处都是高阶函数;
柯里化
- 将一个接收多个参数的函数变换成一系列只接收单个参数的函数;
func add(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }
//将函数add柯里化
func add1(_ v1: Int) -> (Int) -> Int { {$0 + v1} }
- add柯里化后,变成只有一个入参,其返回值也是一个函数;
//实现两个数相加
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }
//add1柯里化
//最完整的写法
func add_y(_ v1: Int) -> (Int) -> Int {
return {
a in
return v1 + a
}
}
func add2(_ v1: Int) -> (Int) -> Int {
{
a in
return v1 + a
}
}
func add_x(_ v1: Int) -> (Int) -> Int {
return {
a in
v1 + a
}
}
func add3(_ v1: Int) -> (Int) -> Int {
{
a in a + v1
}
}
func add4(_ v1: Int) -> (Int) -> Int {
{
$0 + v1
}
}
let r = add3(10)(20)
//实现3个数相加
func add(_ v1: Int,_ v2: Int,_ v3: Int) -> Int { v1 + v2 + v3 }
let r1 = add(10, 20, 30)
print(r1)
//add柯里化
func add5(_ v3: Int) -> (Int) -> (Int) -> Int{
return {
v2 in
return {
v1 in
return v1 + v2 + v3
}
}
}
let k = add5(10)(20)(30)
print(k)
- 实现传一个这种(A,B) -> (C)泛型函数,返回对应的柯里化函数,代码如下所示:
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 } //10+20
////将传入的函数 直接柯里化
func currying(_ fn: @escaping (A,B) -> C) -> (A) -> (B) -> (C) {
return {
a in
return {
b in
return fn(a,b)
}
}
}
let res = currying(add1)(10)(20)
print(res) //30
- 参数为 (A,B) -> C 是一个函数类型,最终返回的是柯里化函数,即只有一个参数,返回值是一个函数的 函数类型 (A) -> (B) -> C;
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 } //10+20
prefix func ~(_ fn: @escaping (A,B) -> C) -> (A) -> (B) -> C
{
return {
a in
return {
b in
return fn(a,b)
}
}
}
let res1 = (~add1)(10)(20)
print(res1)
自定义一个运算符
~
,用来获取函数的柯里化函数的;实现传一个这种(A,B,C) ->D 的泛型函数,返回对应的柯里化函数,代码如下所示:
func add(_ v1: Int,_ v2: Int,_ v3: Int) -> Int { v1 + v2 - v3 }
prefix func ~(_ fn: @escaping(A,B,C) -> D) -> (A) -> (B) -> (C) -> D {
return {
a in
return {
b in
return {
c in
return fn(a,b,c)
}
}
}
}
print((~add)(10)(20)(50)) //-20
函子
- 像Array,optional这样支持
map
运算的类型,称为函子;
public func map(_ transform: (Element) -> T) -> Array
- 取出数组中的元素
Element
,经过transform
函数的逻辑运算,最后再存入数组中;
适用函子
单子
面向协议编程
- 面向协议编程简称POP,是Swift的一种编程范式,Apple与2015年WWDC提出;
- 在Swift标准库中,能见到大量POP的影子;
- Swift也是面向对象的编程语言,简称OOP;
- POP能弥补OOP一些上次的不足;
利用协议实现前缀功能
- 第一个阶段:给定一个字符串,计算出字符为数字的个数,定义一个全局方法
numberCount
var str = "1234test1234"
func numberCount(_ str: String) -> Int {
var count = 0
for c in str {
if (("0"..."9").contains(c)) {
count += 1
}
}
return count
}
print(numberCount(str))
- 第二个阶段:给Swift字符串类String,扩展一个方法;
var str = "1234test1234"
extension String {
var numberCount: Int {
var count = 0
for c in self where ("0"..."9").contains(c){
count += 1
}
return count
}
}
print(str.numberCount)
- 第三个阶段:给字符串的扩展方法
numberCount
,添加指定前缀yy
,表明此拓展方法是自定义的,与系统的区分开来;
struct YY {
var string: String
init(_ string: String) {
self.string = string
}
var numberCount: Int {
var count = 0
for c in string where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
extension String {
var yy: YY {
return YY(self)
}
}
print(str.yy.numberCount)
- 第四个阶段:给所有类型添加指定前缀yy,使用泛型技术来实现;
struct YY {
var base: Base
init(_ base: Base) {
self.base = base
}
}
class Person {}
extension Person {
//计算实例属性
var yy: YY { YY(self) }
//计算类型属性
static var yy: YY.Type { YY.self }
}
extension String {
//计算实例属性
var yy: YY { YY(self) }
//计算类型属性
static var yy: YY.Type { YY.self }
}
struct Dog {}
extension Dog {
//计算实例属性
var yy: YY { YY(self) }
//计算类型属性
static var yy: YY.Type { YY.self }
}
//给具体的类进行扩展
extension YY where Base == String {
//计算属性
var numberCount: Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
//类型方法
static func test() {
print("test")
}
}
extension YY where Base: Person {
//实例方法
func run() {
print("run")
}
//类型方法
static func walk () {
print("walk")
}
}
extension YY where Base == Dog {
func spark() {
print("spark")
}
static func gun() {
print("gun")
}
}
var str: String = "123456ty"
var person: Person = Person()
var dog: Dog = Dog()
print(str.yy.numberCount)
String.yy.test()
person.yy.run()
Person.yy.walk()
dog.yy.spark()
Dog.yy.gun()
- 第五个阶段:添加指定前缀,扩展所有类型,利用协议
YYCompatible
抽取添加前缀属性的方法为公共方法到协议扩展中extension YYCompatible
struct YY {
var base: Base
init(_ base: Base) {
self.base = base
}
}
protocol YYCompatible {}
extension YYCompatible {
//计算实例属性
var yy: YY {
set {}
get { YY(self) }
}
//计算类型属性
static var yy: YY.Type {
set {}
get { YY.self }
}
}
class Person {}
extension Person: YYCompatible{}
extension String: YYCompatible {}
struct Dog {}
extension Dog: YYCompatible {}
//给具体的类进行扩展
extension YY where Base == String {
//计算属性
var numberCount: Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
//类型方法
static func test() {
print("test")
}
}
extension YY where Base: Person {
//实例方法
func run() {
print("run")
}
//类型方法
static func walk () {
print("walk")
}
}
extension YY where Base == Dog {
//mutating表示可以修改YY的内存数据,即YY可修改
mutating func spark() {
print("spark")
}
static func gun() {
print("gun")
}
}
var str = "123456789ss"
var person = Person()
var dog = Dog()
print(str.yy.numberCount)
String.yy.test()
person.yy.run()
Person.yy.walk()
dog.yy.spark()
Dog.yy.gun()
- 第六个阶段:添加指定前缀,扩展所有类型,利用协议
YYCompatible
抽取添加前缀属性的方法为公共方法到协议扩展中extension YYCompatible
- NSString,NSMutableString同样支持numberCount功能;
struct YY {
var base: Base
init(_ base: Base) {
self.base = base
}
}
protocol YYCompatible {}
extension YYCompatible {
//计算实例属性
var yy: YY {
set {}
get { YY(self) }
}
//计算类型属性
static var yy: YY.Type {
set {}
get { YY.self }
}
}
class Person {}
extension Person: YYCompatible{}
struct Dog {}
extension Dog: YYCompatible {}
extension String: YYCompatible {}
extension NSString: YYCompatible {}
//给具体的类进行扩展
extension YY where Base: ExpressibleByStringLiteral {
//计算属性
var numberCount: Int {
var count = 0
for c in (base as! String) where ("0"..."9").contains(c) {
count += 1
}
return count
}
//类型方法
static func test() {
print("test")
}
}
//extension YY where Base: NSString {
// //计算属性
// var numberCount: Int {
// var count = 0
// for c in base as String where ("0"..."9").contains(c) {
// count += 1
// }
// return count
// }
//}
extension YY where Base: Person {
//实例方法
func run() {
print("run")
}
//类型方法
static func walk () {
print("walk")
}
}
extension YY where Base == Dog {
//mutating表示可以修改YY的内存数据,即YY可修改
mutating func spark() {
print("spark")
}
static func gun() {
print("gun")
}
}
var str: String = "123456789ss"
var str1: NSString = "34233222er"
var str2: NSMutableString = "23re21e34"
var person = Person()
var dog = Dog()
print(str.yy.numberCount)
String.yy.test()
print(str1.yy.numberCount)
print(str2.yy.numberCount)
person.yy.run()
Person.yy.walk()
dog.yy.spark()
Dog.yy.gun()
- 因为String,NSString,NSMutableString都遵循
ExpressibleByStringLiteral
协议,所以利用它来定义所有字符串类型,包括OC字符串与Swift字符串;
利用协议进行类型判断
- 判断value是否是数组类型
func isArray(_ value: Any) -> Bool {
// return value is [Any]
return value is Array
}
print(isArray([1,2]))
print(isArray(["1",2]))
print(isArray(NSArray()))
print(isArray(NSMutableArray()))
print(isArray("123456"))
- 判断类型是否是数组类型;
func isArrayType(_ type: Any.Type) -> Bool {
return type is [Any].Type
}
print(isArrayType([Int].self)) //false
print(isArrayType([Any].self)) //true
print(isArrayType(NSArray.self)) //false
print(isArrayType(NSMutableArray.self)) //false
print(isArrayType(String.self)) //false
- 发现这种做法行不通,可使用协议来解决:
protocol ArrayType {}
extension Array: ArrayType {}
extension NSArray: ArrayType {}
////是否是数组类型
func isArrayType(_ type: Any.Type) -> Bool {
return type is ArrayType.Type
}
print(isArrayType([Int].self)) //true
print(isArrayType([Any].self)) //true
print(isArrayType(NSArray.self)) //true
print(isArrayType(NSMutableArray.self)) //true
print(isArrayType(String.self)) //false
响应式编程
- 响应式编程,简称RP;
- 是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定;
- 一般与函数式融合在一起,所以也叫做函数式响应编程,简称FRP;
- 比较成熟的响应式框架有:
- ReactiveCocoa:有OC,Swift版本;
- ReactiveX :有RxJava,RxKotlin,RxSwift,RxPhp,RxGo等等;
RxSwift
- 源码:https://github.com/ReactiveX/RxSwift
- 中文文档:https://beethOven.github.io/RxSwift-Chinese-Documentation/
- Cocoapods安装RxSwift,Podfile文件内容如下:
use_frameworks!
target 'Swift20_RxSwift' do
pod 'RxSwift', '6.2.0'
pod 'RxCocoa', '6.2.0'
end
RxSwift的核心角色
- Observable:负责发送事件(Event);
- Observer:负责订阅Observable,监听Observable发送的事件(Event);
- Event有三种:
@frozen public enum Event {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
import UIKit
import RxCocoa
import RxSwift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//定义一个发送事件器 Observable
let observable = Observable.create { (observer) -> Disposable in
observer.onNext(11)
return Disposables.create()
}
//可以看成是闭包在 订阅监听事件
//Disposable
let disposable = observable.subscribe { (event) in
switch event{
case .next(let element):
print("next",element)
case .error(let error):
print("error",error)
case .completed:
print("completed")
}
}
//取消订阅
disposable.dispose()
observable.subscribe { (element) in
print("next",element)
} onError: { (error) in
print("error",error)
} onCompleted: {
print("completed")
} onDisposed: {
print("disposed")
}.disposed(by: bag) //当bag销毁时,取消订阅
}
}
Disposable
每当observable被订阅的时候,都会返回一个Disposable实例对象,当Disposable实例对象调用dispose,就相当于取消订阅;
在不需要接收事件订阅时,建议取消订阅,释放资源;
实现按钮每隔1秒重复显示/消失;
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//实现每隔1秒钟button显示/消失
//创建发送事件器
let observable = Observable.timer(.seconds(2), period: .seconds(1), scheduler: MainScheduler.instance)
//事件订阅监听者
let binder = Binder(button) { (button, value) in
button.isHidden = value
}
//事件订阅监听者 订阅监听事件
observable.map { $0 % 2 == 0 }.bind(to: binder).disposed(by: bag)
}
}
RxSwift状态监听
- 按钮点击
import UIKit
import RxCocoa
import RxSwift
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
button.rx.controlEvent(.touchUpInside).subscribe(onNext: {
print("按钮点击")
}).disposed(by: bag)
}
}
- Rx实现KVO监听实例对象的存储属性变化;
import UIKit
import RxCocoa
import RxSwift
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
var dog: Dog = Dog()
dog.rx.observe(String.self, "name").subscribe(onNext: { name in
print("name is",name ?? nil)
}).disposed(by: bag)
dog.name = "Tom"
dog.name = "Jack"
}
}
- Rx实现通知监听;
import UIKit
import RxCocoa
import RxSwift
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification).subscribe(onNext: { notification in
print("App进入后台",notification)
}).disposed(by: bag)
}
}
Swift中常见的高阶函数
Map函数
- map函数作用于Collection中的每一个元素,然后返回一个新的Collection;
- map函数可以对数组中的每一个元素做一次处理,如同
遍历
的作用; - map函数接受一个
闭包
作为参数, 然后它会遍历整个数组,并对数组中每一个元素执行闭包中定义的操作
,相当于对数组中的所有元素做了一个映射; - map函数可以对一个集合类型的所有元素做一个映射操作;
- map函数不会修改实例值, 而是新建一个拷贝;
需求一:对数组中的所有元素 大写转小写;
import Foundation
///数组中元素 大写->小写
let strings_00 = ["YAN","ZI"]
var strings_01: [String] = []
///普通常规做法
for str in strings_00 {
strings_01.append(str.lowercased())
}
print(strings_01)
///使用Swift高阶函数 -- Map
///map函数中 闭包最完整的写法
var strings_02 = strings_00.map { (value: String) -> String in
return value.lowercased()
}
print(strings_02)
var strings_03 = strings_00.map { (value) -> String in
return value.lowercased()
}
print(strings_03)
var strings_04 = strings_00.map { (value) in value.lowercased() }
print(strings_04)
///map函数中 闭包最简洁的写法
var strings_05 = strings_00.map { $0.lowercased() }
print(strings_05)
flatMap函数
- flatMap函数依然会遍历数组的元素,并对这些元素执行闭包中定义的操作,与map函数不同的是,它对最终的结果进行了所谓的“压平”操作,
多维数组flatMap之后,变成一维数组
; - flatMap函数返回后,数组的元素会过滤nil值,同时它会把
Optional解包
;
import Foundation
///二维数组的 降维处理
let nums = [[1,2,3],[4,5,6]]
var nums_00 = nums.map{$0}
var nums_01 = nums.flatMap{$0}
print("map: \(nums_00)") //map: [[1, 2, 3], [4, 5, 6]]
print("flatMap: \(nums_01)") //flatMap: [1, 2, 3, 4, 5, 6]
///过滤nil值
let strings = ["yan","zi","fang",nil]
var strings_00 = strings.map({$0})
var strings_01 = strings.compactMap{$0}
print("map: \(strings_00)") //map: [Optional("yan"), Optional("zi"), Optional("fang"), nil]
print("flatMap: \(strings_01)") //flatMap: ["yan", "zi", "fang"]
//可选项
let str_00: String? = String(100)
///默认没有解包操作
let result_00 = str_00.map( {Int($0)} )
///默认有解包操作
let result_01 = str_00.flatMap( {Int($0)} )
print(result_00) //Optional(Optional(100))
print(result_01) //Optional(100)
-
flatMap函数
对数据元素默认有解包操作
,而map函数没有
;
filter函数
- filter函数:创建一个新数组,将满足闭包表达式的元素 添加到新数组中,最终返回新数组;
import Foundation
let nums = [1,2,3,4,5,6]
///过滤出奇数 返回的是奇数数组
let result = nums.filter({ $0 % 2 != 0 })
print(result)
///过滤出偶数 返回的是偶数数组
let result1 = nums.filter( {$0 % 2 == 0} )
print(result1)
forEach函数
- forEach函数:遍历;
import Foundation
let nums = [1,2,3,4,5,6]
///遍历元素
nums.forEach { print($0) }
///遍历元素 索引
nums.enumerated().forEach {
print("index: \($0), element: \($1)")
}
reduce函数
- 首先来查看一下reduce函数底层实现:
@inlinable
public func reduce(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Element) throws -> Result
) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}
-
initialResult
:初始值; -
nextPartialResult
:闭包表达式; -
accumulator
:累加器; - 执行工程:遍历集合,将累加器和元素传入闭包表达式进行指定操作,将结果重新赋值给累加器,最后将累加器返回;
import Foundation
let nums = [1,2,3,4,5]
//最完整的写法
let result = nums.reduce(10) { (pre, current) -> Int in
return pre + current
}
let result01 = nums.reduce(10) { (pre, current) in
pre + current
}
let result02 = nums.reduce(10, { $0 + $1 })
///尾随闭包写法
let result03 = nums.reduce(10) { $0 + $1 }
///简写
let result04 = nums.reduce(10, +)
print(result)
print(result01)
print(result02)
print(result03)
print(result04)
- 在最完整的写法中,数值10是初始值,
pre是每次迭代计算的部分结果
,current每次迭代集合中当前项的值
,最终返回的结果是数组中所有元素叠加与初始化值10的和
; - 其中 10 为累加器的初始值;
- {
$0
+$1
}为闭包表达式,其中$0是累加器的结果值
,$1是数组中遍历元素
;
需求一:集合中的每个元素 * 2,返回一个新的集合
import Foundation
let nums = [1,2,3,4,5]
let result1 = nums.reduce(into: [Int]()) { (numbers, current) in
numbers.append(current * 2)
}
print(result1)
//简写方式
let result2 = nums.reduce(into: [Int]()) {
$0.append($1 * 2)
}
print(result2)
-
[Int]()
:表明reduce函数的返回值类型
是一个整型数组
,最终返回的是numbers数组;
需求二:找出数组中的最大值
import Foundation
let nums = [1,2,100,4,5]
let result1 = nums.reduce(0) { (pre, current) -> Int in
print("\(pre) -- \(current)")
return pre < current ? current : pre
}
print(result1) //100
///简写方式
let result2 = nums.reduce(0) { $0 < $1 ? $1 : $0 }
print(result2) //100
-
pre
:遍历时 每次闭包表达式的返回值; -
current
:遍历时,数组中元素; - pre也是reduce函数 最终的返回值;
需求三:数组逆序
import Foundation
let nums = [1, 2, 3, 4, 5]
let result = nums.reduce([Int]()) { (pres, current) -> [Int] in
return [current] + pres
}
print(result)
let result1 = nums.reduce([Int]()) {
return [$1] + $0
}
print(result1)
/**
accumulator = [Int]() 空数组
数组合并
[element] + []
pres pres
[1] + [] = [1]
[2] + [1] = [2,1]
[3] + [2,1] = [3,2,1]
[4] + [3,2,1] = [4,3,2,1]
[5] + [4,3,2,1] = [5,4,3,2,1]
*/
- 参数
[Int]()
是reduce函数的返回值类型,也是pres的数据类型;
参考文章
Swift底层进阶--018:高阶函数