函数式编程(FP)
/// 把接受两个参数的函数, 变为接受一个参数
let num = 1
func add(_ v: Int) -> (Int) -> Int{
reutrn {
$0 + v
}
}
let fn = add(3)
fn(num)
//简写: add(3)(num) //函数式编程的特征, 先接受一个参数, 再接受一个参数
print(fn(5), fn(100), fn(1000))
/// 函数合成
let fn1 = add(3)
let fn2 = multiple(5)
let fn3 = sub(1)
let fn4 = mod(10)
let fn5 = divide(2)
// 假设要实现以下功能: [(num + 3) * 5 - 1] % 10 / 2
//print(fn5(fn4(fn3(fn2(fn1(num))))))
func composite(_ f1: @escaping (Int) -> Int, f2: @escaping (Int) -> Int) -> (Int) -> Int{
return {
f2(f1($0))
}
}
let fn = composite(add(3), multiple(5))
print(fn(num))//20
//对上面👆优化:
infix operator >>> : AdditionPrecedence //定义>>> 使它跟加法差不多
/*
func >>>(_ f1: @escaping (Int) -> Int, f2: @escaping (Int) -> Int) -> (Int) -> Int{
return {
f2(f1($0))
}
}
*/
func >>><A, B, C>(_ f1: @escaping (A) -> B, f2: @escaping (B) -> C) -> (A) -> C {
return {
f2(f1($0))
}
}
let fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)
print(fn(num))//20
一、函数式编程(FP)-高阶函数
高阶函数至少满足下面的一个条件的函数:
接受一个或多个函数作为输入(map、filter、reduce等)
返回一个函数
二、函数式编程(FP) - 柯里化(Currying)
什么是柯里化? 将一个接受多参数的函数变换为一系列只接受单个参数的函数.
Array、Optional的map函数就是柯里化函数.
给定任何一个函数都能柯里化, 比如下面👇
func add1(_ v1: Int, _ v2: Int) -> Int {v1 + v2}
add1(10, 20)
//柯里化后的效果: add3(20)(10)
func add3(_ v: Int) -> (Int) -> Int { { $0 + v } }
func add2(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }
add2(10, 20, 30)
//柯里化后的效果: add4(30)(20)(10)
func add4(_ v3: Int) -> (Int) -> (Int) -> Int {
return { v2 in //v2 == 20
return { v1 in // v1 == 10
return v1 + v2 + v3
}
}
}
/// 柯里化最终版本: 传一个两个参数的函数过来, 自动柯里化
func currying<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) ->(A) -> C{
return { b in
return { a in
return fn(a, b)
}
}
}
curring(add1)(20)(10)//使用效果
//对上面👆简化:
prefix func ~<A, B, C>(_ fn: @escaping(A, B)-> C) ->(B) ->(A) -> C {
{b in { a in fn(a, b) } }
}
print((~sub)(20)(10))
infix operator >>> : AdditionPrecedence
func >>> <A, B, C>(_ f1: @escaping (A) -> B,
_ f2: @escaping(B) -> C)
-> (A) -> C { { f2(f1($0)) } }
let fn = (~add)(3) >>> (~multiple)(5) >>> (~sub)(1) >>> (~mod)(10) >>> (~divide)(2)
print(fn(1))
/// 柯里化最终版本: 传一个三个参数的函数过来, 实现自动柯里化
prefix func ~<A, B, C, D>(_ fn: @escaping(A, B, C)-> D) ->(C) ->(B) ->(A) -> D {
{c in {b in { a in fn(a, b, c) } } }
}
print((~add2)(30)(20)(10))
三、函数式编程-函子(Functor)
像Array、Optional这样支持map运算的类型, 称为函子(Functor).
// Array<Element>
public func map<T>(_ transform: (Element) ->T) -> Array<T>
// Optional<Wrapped>
public func map<U>(_ transform: (Wrapped) -> U) -> Optional<U>
//怎么样的Type才能称为函子(Functor)?
func map<T>(_ fn: (Inner) -> T) > Type<T>//支持这个运算格式的就是函子
适用函子
四、函数式编程-适用函子(Applicative Functor)
对任意一个函子F, 如果能支持一下运算, 该函子就是一个适用函子
func pure<A>(_ value: A) -> F<A>
func <*><A, B>(fn: F<(A)->B>, value: F<A>) -> F<B>
五、函数式编程-单子(Monad)
对任意一个类型F, 如果能支持一下运算, 那么就可以称为是一个单子(Monad)
func pure<A>(_ value: A) -> F<A>
func flatMap<A, B>(_ value: F<A>, _ fn:(A) -> F<B>) ->F<B>
很显然Array和Optional是单子
面向协议编程(POP)
面向协议编程(Protocol Oriented Programming, 简称POP)
是Swift的一种编程范式, Apple于2015年WWDC提出
在Swift的标准库中, 能见到大量POP的影子
同时, Swift也是一门面向对象的编程语言(Object Oriented Programming, 简称OOP)
在Swift开发中, OOP和POP相辅相成的
OOP的三大特性: 封装、继承、多态
继承的经典使用场景:
当多个类(比如A、B、C类)具有很多共性时, 可以将这些共性抽取到一个父类中(比如D类),最后A、B、C都继承D类
OOP的不足:
比如👇如何将BVC、DVC的公共方法run抽取出来?
class BVC: UIViewController {
func run() {
print("run")
}
}
class DVC: UITableViewController {
func run() {
print("run")
}
}
//基于OOP想到的一些解决方案?❌
1.将run方法放到另一个对象A中, 然后BVC、DVC拥有对象A属性
💣多了一些额外的一类关系
2.将run方法增加到UIViewController分类中
💣UIVIewController会越来越臃肿, 而且会影响它的其他所有子类
3.将run方法抽取到新的父类, 采用多继承? (C++支持多继承)
💣会增加程序设计的复杂度, 产生菱形继承等问题, 需要开发者额外解决
//POP的解决方案:✅
protocol Runnable {
func run()
}
extension Runnable { //Swift支持扩展协议的具体实现(类似Java的接口)
func run() {
print("run")
}
}
class BVC: UIViewController, RunNable {}
class DVC: UITableViewController, Runnable {}
//POP的注意点
优先考虑创建协议, 而不是父类(基类)
优先考虑值类型( struct、enum), 而不是引用类型(class)
巧用协议的扩展功能
不要为了面向协议而使用协议
一、给类拓展功能
var str = "123rrr"
/// 需求: 查找字符串中有几个数字
//方法一: 通过扩展
extension String{
var mj_numberCount: Int {//计算属性
var count = 0
for c in self where ("0"..."9").contains(c){
count += 1
}
return count
}
}
print(str.mj_numberCount)
/// 对上面👆进行优化升级, 效果: print(str.mj.numberCount)
var str = "123rrr"
struct MJ {
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 mj: MJ { return MJ(self) }
}
print("123ooo999".mj.numberCount)
/// 对上面👆进行优化升级, 不能只支持String类型, 让它更通用
struct MJ<Base> {//使用泛型增加通用性
var base: Base
init(_ base: Base){//持有外部传入的参数
self.base = base
}
}
extension String{
var mj: MJ<String> { return MJ(self) }
}
class Person {}
extension Person{
var mj: MJ<Person> { return MJ(self) }
}
//扩展的是对象方法
extension MJ where Base == String { //此处是计算属性, 本质是一个方法
var numberCount: Int {//扩展功能方法
var count = 0
for c in base where ("0"..."9").contains(c){
count += 1
}
return count
}
}
extension MJ where Base: Person {
func run() {//扩展功能方法
print("跑起来了")
}
}
print("12kkk999".mj.numberCount)
Person().mj.run()
/// 对上面👆进行优化升级, 支持拓展类方法. 效果String.mj.test()
//sp1.前缀类型
struct MJ<Base> {//使用泛型增加通用性
var base: Base
init(_ base: Base){//持有外部传入的参数
self.base = base
}
}
//sp2.让想扩充方法的类型, 扩充前缀属性
extension String{
var mj: MJ<String> { MJ(self) }//返回实例属性
static var mj: MJ<String>.Type { return MJ<String>.self } // static属性用类是可以访问出来的, 返回类型属性
//注意: xx类.self 的类型是 xx类.Type. 比如上面 MJ.self 的类型是 MJ.Type
}
//sp3.给前缀扩展方法
extension MJ 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("执行了类方法")
}
}
String.mj.test()
/// 对上面👆sp2进行优化升级, 利用协议protocol扩展前缀属性
//sp1.前缀类型
struct MJ<Base> {//使用泛型增加通用性
var base: Base
init(_ base: Base){//持有外部传入的参数
self.base = base
}
}
//sp2.利用协议protocol扩展前缀属性
protocol MJCompatible {} //协议里面只能声明一些东西,想给协议扩展一些东西的话要用extension
extension MJCompatible{//扩展协议
var mj: MJ<Self> { MJ(self) }//返回实例属性
static var mj: MJ<Self>.Type { MJ<Self>.self } // static属性用类是可以访问出来的, 返回类型属性
//注意: xx类.self 的类型是 xx类.Type. 比如上面 MJ.self 的类型是 MJ.Type
}
//以后谁想给前缀扩展这个功能, 就让让它遵守这个协议
extension String: MJCompatible{} //让String拥有mj前缀属性
//sp3.给String.mj、String().mj前缀扩展功能
extension MJ 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("执行了类方法")
}
}
String.mj.test()
/// 对上面👆进行优化升级, 支持扩展mutating功能 ✅
//sp1.前缀类型
struct MJ<Base> {//使用泛型增加通用性
var base: Base
init(_ base: Base){//持有外部传入的参数
self.base = base
}
}
//sp2.利用协议protocol扩展前缀属性
protocol MJCompatible {} //协议里面只能声明一些东西,想给协议扩展一些东西的话要用extension
extension MJCompatible{//扩展协议
var mj: MJ<Self> {//不要返回只读的计算属性, 为了以后扩展mutating, 换成普通计算属性
set {} //为了mutating语法能够通过
get { MJ(self) }
}
static var mj: MJ<Self>.Type {
set {}
get { MJ<Self>.self }
} // static属性用类是可以访问出来的, 返回类型属性
//注意: xx类.self 的类型是 xx类.Type. 比如上面 MJ.self 的类型是 MJ.Type
}
extension String: MJCompatible{} //让String遵守这个协议
//sp3.给String.mj、String().mj前缀扩展功能
extension MJ where Base == String {
var numberCount: Int {//扩展实例方法
var count = 0
for c in base where ("0"..."9").contains(c){
count += 1
}
return count
}
mutating func run(){//mutating作用是让MJ(结构体)实例是可修改的
//切记常量是不可修改的, 不要用常量调用mutating方法
print("执行了mutating方法")
}
static func test(){//扩展类方法
print("执行了类方法")
}
}
var str = "123321"
str.mj.run()
//注意: 父类扩展了功能, 子类也会拥有这些功能.
二、给NSString、String、NSMutableString同时扩展方法
//NSString、String、NSMutableString共同点: 因为它们都遵循ExpressibleByStringLiteral这个协议,才能用字符串字面量进行初始化.
struct MJ<Base> {
var base: Base
init(_ base: Base){
self.base = base
}
}
protocol MJCompatible {}
extension MJCompatible{
var mj: MJ<Self> {
set {}
get { MJ(self) }
}
static var mj: MJ<Self>.Type {
set {}
get { MJ<Self>.self }
}
}
extension String: MJCompatible{}
extension NSString: MJCompatible{}//NSString遵守了和这个协议, 它的子类NSMutableString也就遵守了这个协议
//代表我这个泛型遵守ExpressibleByStringLiteral这个协议的, 都可以用numberCount方法
extension MJ where Base: ExpressibleByStringLiteral {//Base:后面能放协议、结构体、类型、枚举
var numberCount: Int {
// // 只要是能通过字符串字面量初始化的, 都是能转成Swift的String的
// // 能够通过字符串字面量初始化的只有NSString、String、NSMutableString
// var string = base as! String
var count = 0
for c in (base as! String) where ("0"..."9").contains(c){
count += 1
}
return count
}
mutating func run(){//mutating作用是让MJ(结构体)实例是可修改的
//切记常量是不可修改的, 不要用常量调用mutating方法
print("执行了mutating方法")
}
static func test(){//扩展类方法
print("执行了类方法")
}
}
var str1 = "123321"
var str2: NSString = "123xxx"
var str3: NSMutableString = "123xxx"
print(str1.mj.numberCount)
print(str2.mj.numberCount)
print(str3.mj.numberCount)
三、利用协议实现类型判断
需求1: 判断传入的实例对象是否为数组.
func isArray(_ value: Any)->Bool{
// return value is Array<Any>
return value is [Any] //对上面👆的简写: Any表示数组里可以装入任何东西
}
print(isArray([1, 2]))
print(isArray(["1", 2]))
print(isArray(NSArray())) //OC的类型都是能直接桥接Swift里对应的类型, 例如: NSArray() as Array
print(isArray(NSMutableArray()))
print(isArray("23123"))
需求2: 判断传入的类型是否是数组类型
// 一般判断某个类型是否是数组类型,这么判断:
let type = NSArray.self
print(type is NSArray.Type
//实例中, 判断传入的类型是否是数组类型
//sp1.定义一个协议,让所有数组类型都遵守这个协议
protocol ArrayType {}
extension Array: ArrayType{}
extension NSArray: ArrayType{}
//sp2.判断类型是否是协议的type
func isArrayType(_ type: Any.Type) ->Bool {
// type is [Any].Type//这样写无法正确判断出数组类型, 因为[Any].Type和[Int].type等不是一个东西
//解决方法: 使用协议来完成这个判断
type is ArrayType.Type//协议最终就是一个具体类型
}
//sp3.实战中使用
print(isArrayType([Int].self))
print(isArrayType([Any].self))
print(isArrayType(NSArray.self))
print(isArrayType(NSMutableArray.self))
print(isArrayType(String.self))
//不管是枚举、协议、类、结构体, 都有.Type
//定义ttt这个变量, 表示遵守ArrayType协议的类, 都可以写过来
var ttt: ArrayType.Type //xxx.Type存放的是xxx.self
ttt = Array<Int>.self
ttt = NSArray.self
ttt = NSMutableArray.self
ttt = String.self //报错
小补充:
⚠️传递多个枚举值类型的参数(包装成数组): [xx,xx...]
func run(){
addObserver(self, forKeyPath:"age", options: [.old,.new], context:nil)
}