Files
knowledge-kit/Chapter1 - iOS/1.125.md
2024-04-27 13:01:58 +08:00

5.9 KiB
Raw Blame History

Swift 函数式编程

定义

函数式编程Funtional Programming简称 FP是一种编程范式也就是如何编写程序的方法论

  • 主要思想:把计算过程尽量分解成一系列可复用函数的调用
  • 主要特征:函数是“第一等公民”。函数与其他数据类型一样的地位,可以赋值给其他变量,也可以作为函数参数、函数返回值

函数式编程最早出现在 LISP 语言绝大部分的现代编程语言也对函数式编程做了不同程度的支持比如Haskell、JavaScript、Python、Swift、Kotlin、Scala 等

函数式编程中几个常用的概念:

  • Higher-Order Function、Function Currying
  • Functor、Applicative Functor、Monad

高阶函数

高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入map、filter、reduce等
  • 返回一个函数

FP中到处都是高阶函数

柯里化Currying

将一个接受多个参数的函数变换成为一系列只接受单个参数的函数

Demo

// 函数式编程,为了过程的复用
let num = 10
func add(_ v: Int) -> (Int) -> Int {{ $0 + v }}
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 }}

// 函数合成,范型
infix operator >>> : AdditionPrecedence
func >>><A, B, C>(_ f1: @escaping (A) -> B,
               _ f2: @escaping (B) -> C)
    -> (A) -> C {
    { f2(f1($0)) }
}

// result = ((((x + 3)*5) - 1 )%10)/2
let fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)

print(fn(num))


func multipleAdd(_ v1: Int) -> ((Int) -> ((Int) -> Int))  {
    return { v2 in
        return { v3 in
            return
                v1 + v2 + v3
        }
    }
}

/*
 - multipleAdd(1)调用函数传递1给参数V3此时继续返回一个函数。
 - multipleAdd(1)(2) 拿着上一步返回的函数再去调用传入的2给参数V2.此时继续返回一个函数
 - multipleAdd(1)(2)(3) 拿着上一步返回的函数再去调用此时传入的3给参数V1此时则执行相加操作。V1 + V2 + V3
 */
let rs = multipleAdd(1)(2)(3)
print(rs)

参数对应如下图圈选部分

Demo2将一个三个参数的函数变成柯里化

func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func multipleAdd(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }
func currying<A, B, C>(_ fn: @escaping (A, B) -> C) -> ((A) -> ((B) -> C)) {
    return { a in
        return { b in
            return fn(a, b)
        }
        
    }
}

func currying<A, B, C, D>(_ fn: @escaping (A, B, C) -> D) -> ((A) -> ((B) -> ((C) -> D ))) {
    return { a in
        return { b in
            return { c in
                return fn(a, b, c)
            }
        }
    }
}

let rs1 = currying(add)(10)(20)
let rs2 = currying(multipleAdd)(10)(20)(30)
print(rs1, rs2) // 30 60

函子

像 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)

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

func pure<A>(_ value: A) -> F<A>
func <*><A, B>(fn: F<(A) -> B>, value: F<A>) -> F<B>

Array 可以成为适用函子

func pure<A>(_ value: A) -> [A] {
    [value]
}

infix operator <*> : AdditionPrecedence
func <*><A, B>(fn:[(A) -> B], value: [A]) -> [B] {
    var resultArray: [B] = []
    if fn.count == value.count {
        for i in fn.startIndex..<fn.endIndex {
            resultArray.append(fn[i](value[i]))
        }
    }
    return resultArray
}
print(pure(10))

var array = [{$0 * 2}, {$0 + 10}, {$0 - 5}] <*> [1, 2, 3]
print(array)
// console
[10]
[2, 12, -2]

单子

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

func pure<A>(_ value: A) -> F<A>
func flatMap<A, B>(_ value: F<A>, _ fn: (A) -> F<B>) -> F<B>

很显然Array、Optional 都是单子

“单子”Monad是一个抽象概念它代表了一种设计模式用于组合计算并管理可能包含副作用的值。单子是一种在函数式编程中用于封装和组合计算的通用结构它允许程序员以一致的方式处理各种复杂的计算情况包括错误处理、异步操作、状态管理等。

单子通常定义了几个操作,这些操作允许你以统一的方式对封装在单子中的值进行操作。这些操作包括:

  • returnpure:将一个值“提升”到单子中。
  • bindflatMap:用于组合单子中的计算,允许你将一个单子的输出作为另一个单子的输入。

在 Swift 中,单子并没有像在其他一些语言(如 Haskell 或 Scala中那样作为语言内建的概念但你可以通过定义自己的类型和方法来实现单子模式。例如Swift 中的 Optional 类型可以看作是一个简单的单子,它表示一个值可能存在或不存在。Optional 提供了 mapflatMap 方法,允许你对封装的值进行链式操作。

其他常见的单子实现包括处理异步操作的单子(如 Promise 或 Future处理错误和异常的单子以及管理状态的单子如 State Monad

单子提供了一种强大的方式来管理复杂性和副作用,使代码更易于理解和测试。然而,它们也增加了抽象层次,可能需要一些时间来适应和理解。在 Swift 中,你可以根据自己的需要选择是否使用单子,以及使用哪种类型的单子。