Swift(学习):函数式编程

Array的常见操作

  • Array的map函数:
var arr = [1, 2, 3, 4]
//map映射, 你传入Int,它可以返回Int数组,也可以返回字符串数组
var arr2 = arr.map {
    i in
    return 2 * I
}

var arr3 = arr.map{ $0 * 2 }

var arr33 = arr.map{"abc_\($0)"}


print(arr2) //[2, 4, 6, 8]
print(arr3) //[2, 4, 6, 8]
print(arr33) // ["abc_1", "abc_2", "abc_3", "abc_4"]
  • Array的filter函数:
//filer过滤,遍历数组,返回Bool值,当Bool为YES,将i放入数组arr4
var arr4 = arr.filter {
    i in
    return i % 2 == 0
}

print(arr4) //[2, 4]
  • Array的reduce函数:
//reduce方法的底层是:
/**
 public func reduce<Result>(_ initialResult: Result,
 _ nextPartialResult: (Result, Element) throws -> Result)
 rethrows -> Result
 **/
var arr5 = arr.reduce(0) {
    (result, element) -> Int in
    return result + element
} //这里result第一次表示reduce传入的0, element表示遍历到的数组的每一个元素,第一次是1
//result接下来表示每一次的加和,第二次是result是1, element第二次是2,result是3,依此类推,最终是 0 + 1 + 2 + 3 + 4 = 10,返回值是一个Int


//$0 上一次遍历返回的结果(初始值是10)
//$1 每次遍历到的数组元素
var arr6 = arr.reduce(10) { $0 + $1 }

print(arr5) //10 arr5是Int型
print(arr6) // 20  10 + 1 + 2 + 3 + 4
  • Array的flatMap函数:

flatMap需要每一次遍历返回的是一个数组,并且将数组中元素取出,放入同一个数组并返回, 然而map是可以返回任何值,你返回什么,就将什么放入数组

var arr8 = Array.init(repeating: 3, count: 3)
print(arr8) //[3, 3, 3] repeating表示要重复的数字,count表示重复几次

var arr = [1, 2, 3]
//flatMap需要每一次遍历返回的是一个数组,并且将数组中元素取出,放入同一个数组并返回
//map是可以返回任何值,你返回什么,就将什么放入数组
var arr2 = arr.flatMap { Array.init(repeating: $0, count: $0) }
var arr3 = arr.map { Array.init(repeating: $0, count: $0) }
print(arr2) //[1, 2, 2, 3, 3, 3]
print(arr3) //[[1], [2, 2], [3, 3, 3]]
  • Array的compactMap函数:

compactMap函数会自动将可选类型的元素解包,并将为nil的元素去掉

var arr = ["123", "test", "jack", "-30"]
var arr2 = arr.map{Int($0)} 
var arr3 = arr.compactMap{Int($0)} 
print(arr2) //[Optional(123), nil, nil, Optional(-30)]
print(arr3) //[123, -30]
  • 使用reduce实现map,fileter的功能
var arr = [1, 2, 3, 4]
print(arr.map{$0 * 2}) //[2, 4, 6, 8]
print(arr.reduce([]){
    $0 + [$1 * 2]
}) //[2, 4, 6, 8]

print(arr.filter{ $0 % 2 == 0}) //[2, 4]
print(arr.reduce([]){ $1 % 2 == 0 ? $0 + [$1] : $0 }) //[2, 4]

lazy的优化

  • 未使用lazy的map函数:
let arr = [1, 2, 3]
let result = arr.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-----")
//输出结果:可以看到在还没调用result的时候,已经全部初始化完毕了,很耗性能
//mapping 1
//mapping 2
//mapping 3
//begin-----
//mapped 2
//mapped 4
//mapped 6
//end-----
  • 使用lazy的map函数:
let arr = [1, 2, 3]
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-----")

//输出结果:使用lazy后,可以看到,只有当调用result里的元素时,元素才会对应初始化,节省性能
//begin-----
//mapping 1
//mapped 2
//mapping 2
//mapped 4
//mapping 3
//mapped 6
//end-----

Optional的map和flatMap

  • 当可选型不为nil的时候,会自动解包传入map和flatMap函数,map和flatMap会返回一个可选型,若可选型为nil,则直接返回nil
var num1: Int? = 10
var num2 = num1.map { $0 * 2}

var num3 : Int? = nil
var num4 = num3.map{ $0 * 2 }
print(num2) //Optional(20)
print(num4) //nil
var num1: Int? = 10
var num2 = num1.flatMap { $0 * 2}

var num3 : Int? = nil
var num4 = num3.flatMap{ $0 * 2 }
print(num2) //Optional(20)
print(num4) //nil
  • map函数当你传入一个可选型,它返回的时候会再加一层可选型,但是flatMap函数当你传入一个可选型,它返回的时候如果发现它是可选型,就不会再加一层可选型:
var num1: Int? = 10
var num2 = num1.map{Optional.some($0 * 2)}
var num3 = num1.flatMap{Optional.some($0 * 2)}
print(num2) //Optional(Optional(20))
print(num3) //Optional(20)
var num1: Int? = 10
var num2 = (num1 != nil) ? (num1! + 10) : nil
var num3 = num1.map{$0 + 10}
print(num2) //Optional(20)
print(num3) //Optional(20)

var fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var str: String? = "2011-09-10"
var date1 = str != nil ? fmt.date(from: str!) : nil
var date2 = str.flatMap(fmt.date)
var date3 = str.map {fmt.date(from: $0)}
print(date1) //Optional(2011-09-09 16:00:00 +0000)
print(date2) //Optional(2011-09-09 16:00:00 +0000)
print(date3) //Optional(Optional(2011-09-09 16:00:00 +0000))

if let s = str {
    var a = fmt.date(from: s)
    print(a) //Optional(2011-09-09 16:00:00 +0000)
}

struct Person {
    var name: String
    var age: Int
    init?(_ json: [String: Any]) {
        //逗号, 表示并且的意思,与&&相同
        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]
var p1 = json != nil ? Person(json!) : nil
var p2 = json.flatMap(Person.init)
print(p1) //Optional(TestSwiftCode.Person(name: "Jack", age: 10))
print(p2) //Optional(TestSwiftCode.Person(name: "Jack", age: 10))

函数式编程(Funtional Programming)

  • 函数式编程(Funtional Programming,简称FP)是一种编程范式,也就是如何编写程序的方法论 
  1. 主要思想:把计算过程尽量分解成一系列可复用函数的调用
  2. 主要特征:函数是“第一等公民”
  3. 函数与其他数据类型一样的地位,可以赋值给其他变量,也可以作为函数参数、函数返回值
  • 函数式编程最早出现在LISP语言,绝大部分的现代编程语言也对函数式编程做了不同程度的支持,比如 Haskell、JavaScript、Python、Swift、Kotlin、Scala等
  •  函数式编程中几个常用的概念:Higher-Order Function、Function Currying pFunctor、Applicative Functor、Monad
  • 参考资料:
  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

FP实践 - 传统写法

假设你要实现以下功能: ((num + 3) * 5 - 1) % 10 / 2

FP传统写法:

let num = 1

print(((num + 3) * 5 - 1) % 10 / 2) //4

func add(_ v1: Int, _ v2: Int) -> Int { return v1 + v2 }
func sub(_ v1: Int, _ v2: Int) -> Int { return v1 - v2 }
func multiple(_ v1: Int, _ v2: Int) -> Int { return v1 * v2 }
func divide(_ v1: Int, _ v2: Int) -> Int { return v1 / v2 }
func mod(_ v1: Int, _ v2: Int) -> Int { return v1 % v2 }

var result = divide(mod(sub(multiple(add(num, 3), 5), 1), 10), 2)
print(result) //4

FP函数式写法:

let num = 1
func add(_ v: Int) -> (Int) -> Int { return { $0 + v } }
func sub(_ v: Int) -> (Int) -> Int { return { $0 - v } }
func multiple(_ v: Int) -> (Int) -> Int { return { $0 * v } }
func divide(_ v: Int) -> (Int) -> Int { return { $0 / v } }
func mod(_ v: Int) -> (Int) -> Int { return { $0 % v } }

let fn1 = add(3)
let fn2 = multiple(5)
let fn3 = sub(1)
let fn4 = mod(10)
let fn5 = divide(2)
//当你需要多次用到任意一个数乘以3时,把乘以3变为一个函数
print(fn1(5), fn1(100), fn1(1000))

let result1 = fn5(fn4(fn3(fn2(fn1(num)))))
print(result1)

//函数合成
func composite(_ f1: @escaping (Int) -> Int,
               _ f2: @escaping (Int) -> Int) -> (Int) -> Int

{
    return {  return { f2(f1($0))} }
}

let fn6 = composite(add(3), multiple(5))
print(fn6(num))


infix operator >>>: AdditionPrecedence
func >>>(_ f1: @escaping (Int) -> Int,
               _ f2: @escaping (Int) -> Int) -> (Int) -> Int

{
    return {  return { f2(f1($0))} }
}

let fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)

print(fn(num))

高阶函数(Higher-Order Function)

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


柯里化(currying)

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

//柯里化
func add1(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { return v1 - v2 - v3 }
func add2(_ v3: Int) -> (Int) -> (Int) -> Int {
    // v2 == 20
    return {
        v2 in
        // v1 == 10
        return {
            v1 in
            return v1 - v2 - v3
        }
    }
}

print(add1(30, 20, 10)) //60
print(add2(30)(20)(10)) //60


func add1(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { return v1 - v2 - v3 }
func add2(_ v3: Int) -> (Int) -> (Int) -> Int {
    // v2 == 20
    return {
        v2 in
        // v1 == 10
        return {
            v1 in
            return v1 - v2 - v3
        }
    }
}

print(add1(10, 20, 30)) //-40
  • Array、Optional的map方法接收的参数就是一个柯里化函数

柯里化与函数式编程相结合

示例1:

示例2:


函子(Functor)

  • 像Array、Optional这样支持map运算的类型,称为函子(Functor)
  • 也就是说某个类型调用map,最终返回的还是这个类型,并且这个类型里包装的内容的类型与map函数每次返回的类型相同

我们可以看到在当数组调用map的时候,它返回的内容会直接放入数组中,最后得到的还是数组并且数组里的类型和transform这个函数返回的类型相同,如下:

我们可以看到在当可选型调用map的时候,它返回的内容会包进可选型,最后得到的还是可选型并且可选型里的类型和transform这个函数返回的类型相同,如下:

下面是比较形象的图解:

注意:图上的意思也就是把一个数从一个盒子里拿出来进行一些操作返回后再放入盒子,但是当盒子里为空(为nil),也就会不再进行任何操作


适用函子(Applicative Functor)

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

  • Optional可以成为适用函子

图解:

 注释:把函数和value都从盒子里拿出,进行计算,然后将结果放入盒子

  • Array可以成为适用函子


单子(Monad)

  • 对任意一个类型 F,如果能支持以下运算,那么就可以称为是一个单子(Monad)
  • 很显然,Array、Optional都是单子

Array:

//Swift数组的flatMap底层方法可以简写为 
public func flatMap(_ transform: (A) -> Array<B>) -> Array<B>
//我们可以看出它可以完全转化成单子需要的条件方法:
flatMap(_ value:Array<A> ,_ transform: (A) -> Array<B>) -> Array<B>
//而且重写的逻辑大致相似

Optional:

//Swift数组的flatMap底层方法可以简写为 
public func flatMap(_ transform: (wrapped) -> U?) -> U?
//我们可以看出它可以完全转化成单子需要的条件方法:
flatMap<U>(_value: Optional<Wrapped>, _ transform: (wrapped) -> Optional<U>) -> Optional<U>
//而且重写的逻辑大致相似

猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/99131749