zoukankan      html  css  js  c++  java
  • 闭包 -> map / floatMap / filter / reduce 浅析

    原创: 转载请注明出处

     

    闭包是自包含的函数代码块,可以在代码中被传递和使用

     

    闭包可以捕获存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。

     

    闭包表达式是一种利用简洁语法构建内联闭包的方式

     

    sort 方法(The Sort Method

    Swift 标准库提供了名为sort的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sort(_:)方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被sort(_:)方法修改。

     

    sort(_:)方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。

     

    该例子对一个String类型的数组进行排序,因此排序闭包函数类型需为(String, String) -> Bool

    提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为sort(_:)方法的参数传入:

     

    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

    func backwards(s1: String, s2: String) -> Bool {

        return s1 > s2

    }

    var reversed = names.sort(backwards)

     

    闭包表达式版本

    reversed = names.sort({(s1: String, s2: String) -> Bool in

        return s1 > s2

    })

    该例中sort(_:)方法的整体调用保持不变,一对圆括号仍然包裹住方法的整个参数。然而,参数现在变成了内联闭包。而不再是函数。

     

     

    闭包表达式语法(Closure Expression Syntax

    闭包表达式语法有如下一般形式:

    { (parameters) -> returnType in

        statements

    }

     

    闭包表达式语法可以使用常量、变量和inout类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。

     

    闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始

     

     

    根据上下文推断类型(Inferring Type From Context

    因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:

    reversed = names.sort( { s1, s2 in return s1 > s2 } )

     

    实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。 这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。

     

     

    单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures

    单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:

    reversed = names.sort( { s1, s2 in s1 > s2 } )

    在这个例子中,sort(_:)方法的参数类型明确了闭包必须返回一个Bool类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回Bool类型值,因此这里没有歧义,return关键字可以省略。

     

     

    参数名称缩写(Shorthand Argument Names

    Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。

    如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

    reversed = names.sort{ $0 > $1 }   )

    在这个例子中,$0和$1表示闭包中第一个和第二个String类型的参数。

     

    运算符函数(Operator Functions

     

    实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现:

    reversed = names.sort(>)

     

     

     

    尾随闭包(Trailing Closures

    如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性

     

    func someFunctionThatTakesAClosure(closure: () -> Void) {

        // 函数体部分

    }

     

    // 以下是不使用尾随闭包进行函数调用

    someFunctionThatTakesAClosure({

        // 闭包主体部分

    })

     

    // 以下是使用尾随闭包进行函数调用

    someFunctionThatTakesAClosure() {

        // 闭包主体部分

    }

     

    捕获值(Capturing Values

    闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

     

    注意

    为了优化,如果一个值是不可变的,Swift 可能会改为捕获并保存一份对值的拷贝。

    Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。

     

     

    非逃逸闭包(Nonescaping Closures)

    当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注@noescape,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注@noescape能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。

    func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {

        closure()

    }

    举个例子,sort(_:)方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了@noescape,因为它确保自己在排序结束之后就没用了。

    一种能使闭包逃逸出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:

    var completionHandlers: [() -> Void] = []

    func someFunctionWithEscapingClosure(completionHandler: () -> Void) {

        completionHandlers.append(completionHandler)

    }

    someFunctionWithEscapingClosure(_:)函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你试图将这个参数标注为@noescape,你将会获得一个编译错误。

    将闭包标注为@noescape使你能在闭包中隐式地引用self。

    class SomeClass {

        var x = 10

        func doSomething() {

            someFunctionWithEscapingClosure { self.x = 100 }

            someFunctionWithNoescapeClosure { x = 200 }

        }

    }

     

    let instance = SomeClass()

    instance.doSomething()

    print(instance.x)

    // prints "200"

     

    completionHandlers.first?()

    print(instance.x)

    // prints "100"

     

    闭包引起的循环强引用

    1.循环强引用是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破这些循环强引用

     

    2.循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod()。这两种情况都导致了闭包捕获”self,从而产生了循环强引用。

     

    本质是:两个强引用让彼此一直有效

     

    闭包捕获列表(closure capture list

     

    定义捕获列表

    捕获列表中的每一项都由一对元素组成,一个元素是weak或unowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

    如果闭包有参数列表和返回类型,把捕获列表放在它们前面:

    lazy var someClosure: (Int, String) -> String = {

        [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in

        // 这里是闭包的函数体

    }

    如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方:

    lazy var someClosure: Void -> String = {

        [unowned self, weak delegate = self.delegate!] in

        // 这里是闭包的函数体

    }

     

    弱引用和无主引用

    在闭包和捕获的实例总是互相引用并且总同时销毁时,将闭包内的捕获定义为无主引用

     

    相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。

     

    注意
    如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。

     

     

     

    map,filter,reduce

     

     

    关于SwiftString 、数组 、字典的基本用法这里就不再赘述了,这些都很简单 不会的 在用得时候baidu下就行了。这里主要看下这几个高阶函数

    map

    map方法,其获取一个闭包表达式作为其唯一参数 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值) 具体的映射方式和返回值类型由闭包来指定。

    当提供给数组闭包函数后,map方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。

    来看看map的定义 func map(transform: (T) -> U) -> U[] ,这里 T 和 U 都是泛型 ,指一种类型 , T 和U 只两个不同的类型 ,也可以相同

    来看个例子
    我们用一个Int类型数组存储商品金额,想把每个金额前面添加一个字符“¥

    let prices = [10,20,30]

    let strPrices = prices.map { "¥($0)" }  

     

    这个语法大家应该不陌生吧 ,陌生的去把上节闭包重新看一遍因为map只有一个参数,后面直接用了闭包的尾随 ,不会加括号了

    得到的结果 :print(strPrices) //[¥10, ¥20, ¥30]

    这只是一个简单的实例 ,其实你可以对每个元素进行很复杂的运算,这里不再赘述 ,用法如此,点到为止 、哈哈

    filter

    filter 顾名思义 就是用来过滤的 它使用来选择数组中满足条件的元
    定义:filter(includeElement: (T) -> Bool) -> T[]
    接受一个数组元素 返回一个Bool类型

    let p = [10,20,33,44,87,15]

    let res = p.filter{ $0>20 }

     

    得到结果 :print(res) //[33, 44, 87]
    是不是用起来很方便呀!!

    reduce

    reduce方法把数组元素组合计算为一个值,并且会接受一个初始值,这个初始值得类型可能和数组元素类型不同。来看定义reduce(initial: U, combine: (U, T) -> U) -> U
    不同的大写字母当成不同的类型看待。来看实例

    let p1 = [20,20,10]

    let sum = p1.reduce(0) { $0+$1 }

    print(sum) //50

     

    这个实例给了个初始为Int类型的 0 ,用这个 0 去加数组所有元素

    let sum2 = p1.reduce(4) { $0+$1 }

    print(sum2) //54

     

    初始值换成4 就会得到这样的结果 ,是不是也不是那么难理解呀!

    let sum1 = p1.reduce("2") { " ($0) , ($1)" }

    print(sum1) //  2 , 20 , 20 , 10

     

    如果初始值是一个字串就不能用加法了。不过可以拼接得到上面的结果

    map和filter都很好理解 ,reduce稍微难理解一点

    需要说明的是数据比较大的时候,高阶函数会比传统实现更快,因为它可以并行执行(如运行在多核上),除非真的需要更高定制版本的map,reduce和filter,否则可以一直使用它们以获得更快的执行速度

     

     

     

     

    Swift是一门支持函数式编程的语言,拥有MapFlatMap,Filter,Reduce针对集合类型的操作。在使用Objective-C开发时,如果你没接触过函数式编程,那你可能没听说过这些名词,希望此篇文章可以帮助你了解Swift中的MapFlatMap,Filter,Reduce

    Map

    首先我们来看一下mapSwift中的的定义,我们看到它可以用在 OptionalsSequenceType 上(如:数组、词典等)。

    public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
        /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
        @warn_unused_result
        public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
    }
    
    extension CollectionType {
        /// Returns an `Array` containing the results of mapping `transform`
        /// over `self`.
        ///
        /// - Complexity: O(N).
        @warn_unused_result
        public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
    }

    @warn_unused_result:表示如果没有检查或者使用该方法的返回值,编译器就会报警告。
    @noescape:表示transform这个闭包是非逃逸闭包,它只能在当前函数map中执行,不能脱离当前函数执行。这使得编译器可以明确的知道运行时的上下文环境(因此,在非逃逸闭包中可以不用写self),进而进行一些优化。

    Optionals进行map操作

    简要的说就是,如果这个可选值有值,那就解包,调用这个函数,之后返回一个可选值,需要注意的是,返回的可选值类型可以与原可选值类型不一致:

    ///原来类型: Int?,返回值类型:String?
    var value:Int? = 1
    var result = value.map { String("result = ($0)") }
    /// "Optional("result = 1")"
    print(result)
    var value:Int? = nil
    var result = value.map { String("result = ($0)") }
    /// "nil"
    print(result)
    SequenceType进行map操作

    我们可以使用map方法遍历数组中的所有元素,并对这些元素一一进行一样的操作(函数方法)。map方法返回完成操作后的数组。


     


    我们可以用For-in完成类似的操作:

    var values = [1,3,5,7]
    var results = [Int]()
    for var value in values {
        value *= 2
        results.append(value)
    }
    //"[2, 6, 10, 14]"
    print(results)

    这看起来有点麻烦,我们得先定义一个变量var results然后将values里面的元素遍历,进行我们的操作以后,将其添加进results,我们比较下使用map又会怎么样:

    let results = values.map ({ (element) -> Int in
        return element * 2
    })
    //"[2, 6, 10, 14]"

    我们向map传入了一个闭包,对数组中的所有元素都 乘以2,将返回的新的数组赋值为results,是不是精简了许多?还能更精简!

    精简写法

    let results = values.map { $0 * 2 }
    //"[2, 6, 10, 14]"

    what the fuck...沉住气,让我们一步步来解析怎么就精简成这样了,保证让你神清气爽。翻开The Swift Programming Language中对于闭包的定义你就能找到线索。

    第一步:

    由于闭包的函数体很短,所以我们将其改写成一行:

    let results = values.map ({ (element) -> Int in return element * 2 })
    //"[2, 6, 10, 14]"
    第二步:

    由于我们的闭包是作为map的参数传入的,系统可以推断出其参数与返回值,因为其参数必须是(Element) -> Int类型的函数。因此,返回值类型,->及围绕在参数周围的括号都可以被忽略:

    let results = values.map ({ element  in return element * 2 })
    //"[2, 6, 10, 14]"
    第三步:

    单行表达式闭包可以通过省略return来隐式返回闭包的结果:

    let results = values.map ({ element  in element * 2 })
    //"[2, 6, 10, 14]"

    由于闭包函数体只含有element * 2这单一的表达式,该表达式返回Int类型,与我们例子中map所需的闭包的返回值类型一致(其实是泛型),所以,可以省略return

    第四步:

    参数名称缩写(Shorthand Argument Names),由于Swift自动为内联闭包提供了参数缩写功能,你可以直接使用$0,$1,$2...依次获取闭包的第1,2,3...个参数。
    如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略:

    let results = values.map ({ $0 * 2 })
    //"[2, 6, 10, 14]"

    例子中的$0即代表闭包中的第一个参数。

    最后一步:

    尾随闭包,由于我们的闭包是作为最后一个参数传递给map函数的,所以我们可以将闭包表达式尾随:

    let results = values.map (){ $0 * 2 }
    //"[2, 6, 10, 14]"

    如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉:

    let results = values.map { $0 * 2 }
    //"[2, 6, 10, 14]"

    如果还有不明白的,可以多翻阅翻阅The Swift Programming Language

    FlatMap

    与map一样,它可以用在 OptionalsSequenceType 上(如:数组、词典等)。我们先来看看针对Optional的定义:

    Optionals进行flatMap操作
    public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
        /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
        @warn_unused_result
        public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
    }

    就闭包而言,这里有一个明显的不同,这次flatMap期望一个 (Wrapped) -> U?)闭包。对于可选值, flatMap 对于输入一个可选值时应用闭包返回一个可选值,之后这个结果会被压平,也就是返回一个解包后的结果。本质上,相比 map,flatMap也就是在可选值层做了一个解包。

    var value:String? = "1"
    var result = value.map { Int($0)}
    /// "Optional(Optional(1))"
    print(result)
    var value:String? = "1"
    var result = value.flatMap { Int($0)}
    /// ""Optional(1)"
    print(result)

    使用flatMap就可以在链式调用时,不用做额外的解包工作:

    var value:String? = "1"
    var result = value.flatMap { Int($0)}.map { $0 * 2 }
    /// ""Optional(2)"
    print(result)
    SequenceType进行flatMap操作

    我们先来看看Swift中的定义

    extension SequenceType {
        /// 返回一个将变换结果连接起来的数组
        /// `transform` over `self`.
        ///     s.flatMap(transform)
        /// is equivalent to
        ///     Array(s.map(transform).flatten())
        @warn_unused_result
        public func flatMap<S : SequenceType>(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element]
    }
    
    extension SequenceType {
        /// 返回一个包含非空值的映射变换结果
        @warn_unused_result
        public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
    }

    通过这两个描述,就提现了flatMapSequenceType的两个作用:

    一:压平
    var values = [[1,3,5,7],[9]]
    let flattenResult = values.flatMap{ $0 }
    /// [1, 3, 5, 7, 9]
    二:空值过滤
    var values:[Int?] = [1,3,5,7,9,nil]
    let flattenResult = values.flatMap{ $0 }
    /// [1, 3, 5, 7, 9]

    Filter

    同样,我先来看看Swift中的定义:

    extension SequenceType {
        /// 返回包含原数组中符合条件的元素的数组
        /// Returns an `Array` containing the elements of `self`,
        /// in order, that satisfy the predicate `includeElement`.
        @warn_unused_result
        public func filter(@noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element]
    }

    filter函数接受一个(Element) -> Bool)的闭包,来判断原数组中的元素是否符合条件,这个方法用来过滤数组中的一些元素再好不过了:

    var values = [1,3,5,7,9]
    let flattenResults = values.filter{ $0 % 3 == 0}
    //[3, 9]

    我们向flatMap传入了一个闭包,筛选出了能被3整除的数据。

    Reduce

    我们先来看下Swift中的定义:

    extension SequenceType {
        /// Returns the result of repeatedly calling `combine` with an
        /// accumulated value initialized to `initial` and each element of
        /// `self`, in turn, i.e. return
        /// `combine(combine(...combine(combine(initial, self[0]),
        /// self[1]),...self[count-2]), self[count-1])`.
        @warn_unused_result
        public func reduce<T>(initial: T, @noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> T
    }

    给定一个初始化的combine结果,假设为result,从数组的第一个元素开始,不断地调用combine闭包,参数为:(result,数组中的元素),返回的结果值继续调用combine函数,直至元素最后一个元素,返回最终的result值。来看下面的代码(为了更方便你理解这个过程,代码就不简写了):

    var values = [1,3,5]
    let initialResult = 0
    var reduceResult = values.reduce(initialResult, combine: { (tempResult, element) -> Int in
        return tempResult + element
    })
    print(reduceResult)
    //9

    我们存在一个数组[1,3,5],给定了一个初始化的结果 initialResult = 0,向reduce函数传了 (tempResult, element) -> Int的闭包,tempResut便是每次闭包返回的结果值,并且其初始值为我们之前设置的initialResult0element即为我们数组中的元素(可能为1,3,5)。reduce会一直调用combine闭包,直至数组最后一个元素。下面的代码更形象地描述了整个过程,这其实跟reduce所做的操作是等价的:

    func combine(tempResult: Int, element: Int) -> Int  {
        return tempResult + element
    }
    reduceResult = combine(combine(combine(initialResult, element: 1), element: 3), element: 5)
    print(reduceResult)
    //9

    以上所用的一些示例代码可以在我的Github中找到,如果您有什么建议可以在评论区留言。



     

     

    Map

    map函数能够被数组调用,它接受一个闭包作为参数,作用于数组中的每个元素。闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组

    1. 比如我们有一个这样的需求遍历一个数组中所有的元素,将每个元素自身与自身相加,最后返回一个保存相加后元素的数组(-_-原谅我这表达能力,下面用代码阐述)

    如果我们不使用map函数,那么代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    let numbers = [1,2,3]
    var sumNumbers = [Int]()
    for var number in numbers {
        number += number
        sumNumbers.append(number)
    }
    // [2,4,6]
    print(sumNumbers)

    可以看到上面的代码正是我们经常用到的代码,但通过数组的map函数可以帮我们简化上面的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 可以看到我们甚至可以不再定义可变的数组直接用不可变的就可以
    let numbers = [1,2,3]
    let sumNumbers = numbers.map { (number: Int) -> Int in
        return number + number
    }
    // 下面介绍简便写法 因为map闭包里面的类型可以自动推断所以可以省略
    let sumNumbers1 = numbers.map { number in
        return number + number
    }
    // 可以省了return 但是循环次数多了一次 目前不知道这是什么原因(循环次数是3次这是4次) 结果是一样的 
    let sumNumbers2 = numbers.map { number in number + number }
    print(sumNumbers2) // [2,4,6]
    // 最终简化写法
    let sumNumbers3 = numbers.map { $0 + $0 }

    2. Map函数返回数组的元素类型不一定要与原数组相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let fruits = ["apple""banana""orange"""]
    // 这里数组中存在一个""的字符串 为了后面来比较 map 和 flatMap
    let counts = fruits.map { fruit -> Intin
        let length = fruit.characters.count
        guard length > 0 else {
            return nil
        }
        return length
    }
    // [Optional(5), Optional(6), Optional(6), nil]
    print(counts)

    3. Map还能返回判断数组中的元素是否满足某种条件的Bool值数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let array = [1,2,3,4,5,6]
    // 最洁简的写法
    let isEven = array.map { $0 % 2 == 0 }
    // 这里在写下完成的写法 下面的例子 将都采用最洁简的写法^_^ 同时也要养成习惯看见上面那种洁简的写法 就要懂它做了些什么 会有什么样的结果
    let isEven1 = array.map { num in
        // 写上retrun在Playground中的循环次数是6次 不写是7次 Xcode版本是7.2(7C68) 
        // 不知道这是不是bug 有知道的提醒下我~
        return num % 2 == 0
    }
    // [false, true, false, true, false, true]
    print(isEven)

    FlatMap

    flatMap 与 map 不同之处是
    flatMap返回后的数组中不存在 nil 同时它会把Optional解包;
    flatMap 还能把数组中存有数组的数组 一同打开变成一个新的数组 ;
    flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积

    1. flatMap返回后的数组中不存在nil 同时它会把Optional解包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let fruits = ["apple""banana""orange"""]
    let counts = fruits.flatMap { fruit -> Intin
        let length = fruit.characters.count
        guard length > 0 else {
            return nil
        }
        return length
    }
    // [5,6,6]
    print(counts)

    2. flatMap 还能把数组中存有数组的数组 一同打开变成一个新的数组(看代码秒懂~_~)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let array = [[1,2,3], [4,5,6], [7,8,9]]
    // 如果用map来获取新的数组
    let arrayMap = array.map { $0 }
    // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    print(arrayMap)
    // 用flatMap
    let arrayFlatMap = array.flatMap { $0 }
    // [1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(arrayFlatMap)

    3. flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 这种情况是把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积
    let fruits = ["apple""banana""orange"]
    let counts = [1, 2, 3]
    let fruitCounts = counts.flatMap { count in
        fruits.map { fruit in
    //        title + "(index)"
            // 也可以返回元组
            (fruit, count)
        }
    }
    // [("apple", 1), ("banana", 1), ("orange", 1), ("apple", 2), ("banana", 2), ("orange", 2), ("apple", 3), ("banana", 3), ("orange", 3)]
    print(fruitCounts)
    // 这种方法估计用的很少 可以算是一个 flatMap 和 map 的结合使用吧

    Filter

    filter 可以取出数组中符合条件的元素 重新组成一个新的数组

    filter可以很好的帮我们把数组中不需要的值都去掉 这个很赞!

    1
    2
    3
    4
    let numbers = [1,2,3,4,5,6]
    let evens = numbers.filter { $0 % 2 == 0 }
    // [2, 4, 6]
    print(evens)

    Reduce

    map,flatMap和filter方法都是通过一个已存在的数组,生成一个新的、经过修改的数组。然而有时候我们需要把所有元素的值合并成一个新的值 那么就用到了Reduce

    1. 比如我们要获得一个数组中所有元素的和

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let numbers = [1,2,3,4,5]
    // reduce 函数第一个参数是返回值的初始化值
    let sum = numbers.reduce(0) { $0 + $1 }
    // 这里我写下完整的格式
    let sum1 = numbers.reduce(0) { total, num in
        // 这里写不写return在Playground都循环5次 但上面用最洁简的方法显示循环6次。。。 What The Fuck 这是什么鬼!!!
        return total + num
    }
    // 15
    print(sum)

    2. 合并成的新值不一定跟原数组中元素的类型相同

    1
    2
    3
    4
    5
    let numbers = [1,5,1,8,8,8,8,8,8,8,8]
    // reduce 函数第一个参数是返回值的初始化值
    let tel = numbers.reduce("") { "($0)" "($1)" }
    // 15188888888
    print(tel)

    3. ruduce 还可以实现 map 和 filter 并且时间复杂度变为O(n) 原来 map 和 filter 的时间复杂度是O(n*n)

    1
    2
    3
    4
    5
    6
    7
    8
    extension Array {
        func mMap (transform: Element -> U) -> [U] {
            return reduce([], combine: { $0 + [transform($1)] })
        }
        func mFilter (includeElement: Element -> Bool) -> [Element] {
            return reduce([]) { includeElement($1) ? $0 + [$1] : $0 }
        }
    }

    参考链接:
    1.http://www.jianshu.com/p/87b97dfbf17b
    2.http://www.jianshu.com/p/7233f140e6c3
  • 相关阅读:
    Android——Activity去除标题栏和状态栏
    Android——程序员的情怀——优化BaseAdapter
    Android——Android Sutido:[2]导入eclipse项目篇
    【Android开源项目分析】自定义圆形头像CircleImageView的使用和源码分析
    学佛略要
    Keystone, Start, Failed to Load Bson
    又梦见了你
    伦敦之旅
    无题
    Multiverse in Doctor Strange // Multiverse在《神秘博士》
  • 原文地址:https://www.cnblogs.com/Jenaral/p/5647288.html
Copyright © 2011-2022 走看看