Swift第十二章 函数式编程

Array的map与flatMap

Array的常见操作

  1. 代码示例

     var arr = [1, 2, 3, 4]
     // [2, 4, 6, 8],遍历数组每一个元素,然后对每个元素进行处理,最后生成一个新的数组
     var arr2 = arr.map { $0 * 2 }
        
     // [2, 4],过滤,遍历数组每一个元素,然后能够被2整除的数保留,最后生成一个新的数组
     var arr3 = arr.filter { $0 % 2 == 0 }
        
     //遍历数组每一个元素,reduce(0)中的为初始值0,用每个元素+初始值结果作为下一次遍历的初始值,然后将最后累加结果作为返回值。
     // 10  $0上一次遍历返回的结果(初始值为0),$1每次遍历的元素值
     var arr4 = arr.reduce(0) { $0 + $1 }
     // 10
     var arr5 = arr.reduce(0, +)
        
     func double(_ i: Int) -> Int { i * 2 }
     var arr = [1, 2, 3, 4]
     // [2, 4, 6, 8] 将double作为参数传递给map
     print(arr.map(double))
        
     var arr = [1, 2, 3]
     // [[1], [2, 2], [3, 3, 3]]
     var arr2 = arr.map { Array.init(repeating: $0, count: $0) }
     // [1, 2, 2, 3, 3, 3]  ,将所有数组都合并成一个数组
     var arr3 = arr.flatMap { Array.init(repeating: $0, count: $0) }
        
     var arr = ["123", "test", "jack", "-30"]
     // [Optional(123), nil, nil, Optional(-30)]
     var arr2 = arr.map { Int($0) }
     // [123, -30]  只保存,转化成功的结果
     var arr3 = arr.compactMap { Int($0) } 
        
     // 使用reduce实现map、filter的功能
     var arr = [1, 2, 3, 4]
     // [2, 4, 6, 8]
     print(arr.map { $0 * 2 })
     print(arr.reduce([]) { $0 + [$1 * 2] })
        
     // [2, 4]
     print(arr.filter { $0 % 2 == 0 })
     print(arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 })
    

lazy的优化

  1. 示例

     let arr = [1, 2, 3]
     //使用了lazy之后,可以发现当需要真正使用到result的某个元素之后,才会真正的去map遍历
     let result = arr.lazy.map {
     (i: Int) -> Int in
     print("mapping \(i)")
     return i * 2
     }
     print("begin-----")
     print("mapped", result[0])
     print("mapped", result[1])
     print("mapped", result[2])
     print("end----")
    
  2. 打印:

     begin----
     mapping 1
     mapped 2
     mapping 2
     mapped 4
     mapping 3
     mapped 6
     end----
    

Optional的map和flatMap

  1. 示例

     var num1: Int? = 10
     // Optional(20) 将可选类型解包,然后执行闭包,然后将发回执包装成可选类型返回。
     var num2 = num1.map { $0 * 2 }
        
     //可选类型解包如果为nil,那么就不在执行闭包,直接返回nil
     var num3: Int? = nil
     // nil
     var num4 = num3.map { $0 * 2 }
        
     //map 与 flatMa的区别
     var num1: Int? = 10
     // Optional(Optional(20))  ,map会将返回结果一定进行Optional包装
     var num2 = num1.map { Optional.some($0 * 2) }
     // Optional(20) ,flatMap如果发现返回结果是Optional,则不会再次进行包装
     var num3 = num1.flatMap { Optional.some($0 * 2) }
        
     //使用场景1
     var num1: Int? = 10
     var num2 = (num1 != nil) ? (num1! + 10) : nil
     var num3 = num1.map { $0 + 10 }
     // num2、num3是等价的
        
     //使用场景2
     var fmt = DateFormatter()
     fmt.dateFormat = "yyyy-MM-dd"
     var str: String? = "2011-09-10"
     // old
     var date1 = str != nil ? fmt.date(from: str!) : nil
     // new 与上面old等价
     //var date2 = str.flatMap(fmt.date(from:$0))
     var date2 = str.flatMap(fmt.date)
        
     //使用场景3
     var score: Int? = 98
     // old
     var str1 = score != nil ? "socre is \(score!)" : "No score"
     // new
     var str2 = score.map { "score is \($0)" } ?? "No score"
    
  2. 案例2

     struct Person {
         var name: String
         var age: Int
     }
     var items = [
         Person(name: "jack", age: 20),
         Person(name: "rose", age: 21),
         Person(name: "kate", age: 22)
     ]
     // old
     func getPerson1(_ name: String) -> Person? {
         let index = items.firstIndex { $0.name == name }
         return index != nil ? items[index!] : nil
     }
     // new
     func getPerson2(_ name: String) -> Person? {
         return items.firstIndex { $0.name == name }.map { items[$0] }
     }
    
  3. 案例3

     struct Person {
         var name: String
         var age: Int
         init?(_ json: [String : Any]) {
             // let 条件 用逗号分隔,表示且
             guard let name = json["name"] as? String,
             let age = json["age"] as? Int else {
                 return nil
             }
             self.name = name
             self.age = age
         }
     }
     var json: Dictionary? = ["name" : "Jack", "age" : 10]
     // old
     var p1 = json != nil ? Person(json!) : nil
     // new
     var p2 = json.flatMap(Person.init)
    

函数式编程(Funtional Programming)

  1. 函数式编程(Funtional Programming,简称FP)是一种编程范式,也就是如何编写程序的方法论
  2. 主要思想:把计算过程尽量分解成一系列可复用函数的调用
  3. 主要特征:函数与其他数据类型一样的地位,可以赋值给其他变量,也可以作为函数参数、函数返回值
  4. 函数式编程最早出现在LISP语言,绝大部分的现代编程语言也对函数式编程做了不同程度的支持,比如Haskell、JavaScript、Python、Swift、Kotlin、Scala等
  5. 函数式编程中几个常用的概念
    1. Higher-Order Function、Function Currying
    2. Functor、Applicative Functor、Monad
  6. 参考资料
    1. http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
    2. http://www.mokacoding.com/blog/functor-applicative-monads-in-pictures

引入

  1. 传统写法: 假设要实现以下功能:[(num + 3) * 5 - 1] % 10 / 2

     // 假设要实现以下功能:[(num + 3) * 5 - 1] % 10 / 2
     var num = 1
     func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
     func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
     func multiple(_ 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 }
        
     //调用
     divide(mod(sub(multiple(add(num, 3), 5), 1), 10), 2)
    
  2. 函数式写法

     //1. 将上面的函数都改造成只接收一个参数:每个函数返回值为函数
     func add(_ v: Int) -> (Int) -> Int { { $0 + v } } //2+3调用add(2)(3) 
     func sub(_ v: Int) -> (Int) -> Int { { $0 - v } }
     func multiple(_ v: Int) -> (Int) -> Int { { $0 * v } }
     func divide(_ v: Int) -> (Int) -> Int { { $0 / v } }
     func mod(_ v: Int) -> (Int) -> Int { { $0 % v } }
        
     //2. 函数合成
     let fn1 = add(3)
     let fn2 = multiple(5)
     let fn3 = sub(1)
     let fn4 = divide(10)
     let fn5 = mod(2)
     //[(num + 3) * 5 - 1] % 10 / 2
     fn5(fn4(fn3(fn2(fn1(num)))))
        
     func composite(_ f1: @escaping (Int) -> Int, _ f2: @escaping (Int) -> Int) -> (Int) -> Int {
         return {f2(f1($0))}
     }
     //将2个函数合成1个函数
     let fn = composite(fn1, fn2)
     print(fn(num))
        
     //将以上composite通过>>>重载运算符实现
     infix operator >>> : AdditionPrecedence
     func >>>(_ f1: @escaping (Int) -> Int, _ f2: @escaping (Int) -> Int) -> (Int) -> Int {
         return {f2(f1($0))}
     }
     let fn = fn1 >>> fn2
     print(fn(num))
        
     let fnx = add(3)>>>multiple(5)>>>sub(1)>>>divide(10)>>>mod(2)
     print(fnx(num))
        
        
     //封装成通用的泛型
     infix operator >>> : AdditionPrecedence
     func >>><A, B, C>(_ f1: @escaping (A) -> B,
     _ f2: @escaping (B) -> C) -> (A) -> C { { f2(f1($0)) } }
     var fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)
     fn(num)
    

高阶函数(Higher-Order Function)

  1. 高阶函数是至少满足下列一个条件的函数:
    1. 接受一个或多个函数作为输入(map、filter、reduce等)。
    2. 返回一个函数。
  2. FP(函数式编程)中到处都是高阶函数

    func add(_ v: Int) -> (Int) -> Int { { $0 + v } }
    

柯里化(Currying)

  1. 什么是柯里化?将一个接受多参数的函数变换为一系列只接受单个参数的函数

     //原始函数
     func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
     add(10, 20)
        
     //柯里化之后
     func add(_ v: Int) -> (Int) -> Int { { $0 + v } }
     add(10)(20)
    
  2. Array、Optional的map方法接收的参数就是一个柯里化函数
  3. 案例1:

     //原函数
     func add1(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
     func add2(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }
        
     //柯里化后
     func add1(_ v: Int) -> (Int)-> Int { { $0 + v} }
     add1(10)(20)
        
     func add2(_ v3:Int) -> (Int) -> (Int) -> Int {
         // v2 == 20
         return { v2 in
             // v1 == 10
             return { v1 in
                 return v1 + v2 + v3
             }
         }
     }
     add2(10)(20)(30)
        
     //封装柯里化器:传入一个函数,然后返回柯里化后的函数
     func currying<A, B, C>(_ fn: @escaping (A, B) -> C)
     -> (B) -> (A) -> C {
         { b in { a in fn(a, b) } }
     }
     func currying<A, B, C, D>(_ fn: @escaping (A, B, C) -> D)
     -> (C) -> (B) -> (A) -> D {
         { c in { b in { a in fn(a, b, c) } } }
     }
        
     let curriedAdd1 = currying(add1)
     print(curriedAdd1(10)(20))
     let curriedAdd2 = currying(add2)
     print(curriedAdd2(10)(20)(30))
    
  4. 案例2:

     func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
     func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
     func multiple(_ 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 }
        
     //封装柯里化器:传入一个函数,然后返回柯里化后的函数
     prefix func ~<A, B, C>(_ fn: @escaping (A, B) -> C)
     -> (B) -> (A) -> C { { b in { a in fn(a, b) } } }
        
     infix operator >>> : AdditionPrecedence
    func >>><A, B, C>(_ f1: @escaping (A) -> B,_ f2: @escaping (B) -> C) -> (A) -> C { { f2(f1($0)) } }
    
     var num = 1
     var fn = (~add)(3) >>> (~multiple)(5) >>> (~sub)(1) >>> (~mod)(10) >>> (~divide)(2)
     fn(num)
    

函子(Functor)

  1. 像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>
    

适用函子(Applicative Functor)

  1. 对任意一个函子 F,如果能支持以下运算,该函子就是一个适用函子

     func pure<A>(_ value: A) -> F<A>
     func <*><A, B>(fn: F<(A) -> B>, value: F<A>) -> F<B>
    
  2. Optional可以成为适用函子

     //Optional实现下面方法
     func pure<A>(_ value: A) -> A? { value }
     infix operator <*> : AdditionPrecedence
     func <*><A, B>(fn: ((A) -> B)?, value: A?) -> B? {
         guard let f = fn, let v = value else { return nil }
         return f(v)
     }
        
     //调用
     var value: Int? = 10
     var fn: ((Int) -> Int)? = { $0 * 2}
     // Optional(20)
     print(fn <*> value as Any)
    
  3. Array可以成为适用函子

     //Array实现下面方法
     func pure<A>(_ value: A) -> [A] { [value] }
     func <*><A, B>(fn: [(A) -> B], value: [A]) -> [B] {
         var arr: [B] = []
         if fn.count == value.count {
             for i in fn.startIndex..<fn.endIndex {
                 arr.append(fn[i](value[i]))
             }
         }
         return arr
     }
        
     //调用
     // [10]
     print(pure(10))
     var arr = [{ $0 * 2}, { $0 + 10 }, { $0 - 5 }] <*> [1, 2, 3]
     // [2, 12, -2]
     print(arr)
    

单子(Monad)

  1. 对任意一个类型 F,如果能支持以下运算,那么就可以称为是一个单子(Monad)

     func pure<A>(_ value: A) -> F<A>
     func flatMap<A, B>(_ value: F<A>, _ fn: (A) -> F<B>) -> F<B>
    
  2. 很显然,Array、Optional都是单子

Table of Contents