内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

Swift中使用reduce函数的一个小示例

2018-06-08 19:55 出处:清屏网 人气: 评论(0

今天看到一篇通过一个比较有意思的示例来讲解 reduce 函数使用的文章 reduce all the things ,自己也依葫芦画瓢的在 Swift 4.0 下实现了一遍 :)

示例大致是这样的:

如下为美国各个城市的 persons 列表,其中每个 person 的结构包括姓名、所属城市(其中城市名字符串的逗号之后为州名,如 CA 为加利福尼亚州)、平均年龄。请使用 map/flatmap/filter/reduce 等函数来封装一个查询指定州的总人数和平均年龄,其中函数输入参数为州名,persons 列表。

let persons: [[String: Any]] = [
    ["name": "Carl Saxon", "city": "New York, NY", "age": 44],
    ["name": "Travis Downing", "city": "El Segundo, CA", "age": 34],
    ["name": "Liz Parker", "city": "San Francisco, CA", "age": 32],
    ["name": "John Newden", "city": "New Jersey, NY", "age": 21],
    ["name": "Hector Simons", "city": "San Diego, CA", "age": 37],
    ["name": "Hector Simons", "city": "Douglas County, CO", "age": 39],
    ["name": "Brian Neo", "age": 27],
]

这个示例如果是用 map/flatmap/filter 等函数组合起来也是可以实现的,只是比较啰嗦不够优雅,而且在这个 persons 量级上升后,性能也会不那么乐观(主要涉及到对整个 persons 的遍历次数,使用 reduce 只需要对 persons 遍历一次,具体可以参考那篇文章)。

先来简单看下 reduce 在 Swift 中的定义:

/// Returns the result of combining the elements of the sequence using the
/// given closure.
///
/// Use the `reduce(_:_:)` method to produce a single value from the elements
/// of an entire sequence. For example, you can use this method on an array
/// of numbers to find their sum or product.
///
/// The `nextPartialResult` closure is called sequentially with an
/// accumulating value initialized to `initialResult` and each element of
/// the sequence. This example shows how to find the sum of an array of
/// numbers.
///
/// ... ...
///
/// - Parameters:
///   - initialResult: The value to use as the initial accumulating value.
///     `initialResult` is passed to `nextPartialResult` the first time the
///     closure is executed.
///   - nextPartialResult: A closure that combines an accumulating value and
///     an element of the sequence into a new accumulating value, to be used
///     in the next call of the `nextPartialResult` closure or returned to
///     the caller.
/// - Returns: The final accumulated value. If the sequence has no elements,
///   the result is `initialResult`.
public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

在注释中很清晰的描述了 reduce 的算法逻辑,给定一个初始化的 result,然后不断的通过调用 nextPartialResult 闭包来计算下一个 result,该闭包的参数为 (上一次执行后的 result 和 collection 中的元素),然后返回的 Result 再继续调用 nextPartialResult 闭包,直到返回最终的 result 值。

下面是在 swift 4.0 上的实现代码:

/// 从 persons 人口列表中查询指定州(state)的(人口总数和平均年龄)
///
/// - state: 州缩写,如:CA
/// - persons: 需要查询的总人口列表
func query(state: String, persons: [[String: Any]]) -> (count: Int, age: Float) {
    
    // 使用类型别名,让代码看上去更简洁易懂些
    typealias Acc = (count: Int, age: Float)
    
    // 将 reduce 后的结果(为一个 tuple)存储为临时变量
    // 结果中的 count 为指定州的总人口,age 为总的年龄书
    let u = persons.reduce((count: 0, age: 0.0)) { (acc: Acc, person) -> Acc in
        guard let personState = (person["city"] as? String)?.components(separatedBy: ", ").last,
            let personAge = person["age"] as? Int,
            personState == state
            
            // 如果没有城市(city),也没有年龄(age),
            // 或者非指定的州,则直接返回当前的 acc
            else { return acc}
        return (count: acc.count + 1, age: acc.age + Float(personAge))
    }
    
    // 最终再计算出平均年龄
    return (count: u.count, age: u.age / Float(u.count))
}

这里使用 tuple 作为 accumulator 来将关联数据与 reduce 关联起来。

调用时只需要传入需要查询的州缩写(如:CA)即可查询该州的人口总数和平均年龄:

query(state: "CA", persons: persons)  // (count: 3, age: 37.0)
query(state: "NY", persons: persons)  // (count: 2, age: 36.0)

分享给小伙伴们:
本文标签: reduceSwift

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号