使用reduce实现map和filter(swift、泛型和闭包)

tags: swift, 函数式, 闭包, 泛型

其实要拍脑袋直接写好像也不难。
但是还是尝试一步步分解地写下来,这样的话问题就更加有普遍性。
因为并不是所有问题都能一步到位地解决,重要的可能不是问题本身,而是解决问题的步骤和方式,这才是需要精深练习的地方。

Map

1、先写测试用例

假设我自己实现的map函数名为ptxMap,假如ptxMap的结果和sdk实现的map方法返回结果是相同的,就可以认为成功啦。

testcase1:

let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

let resultA = testArray.map() { $0 * 2 }
let resultB = testArray.ptxMap() { $0 * 2 }
assert(resultA == resultB, "two results should equal")
//[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
//testcase1 fail

2、根据测试用例编写第一个版本的代码

第一个可能觉得生涩的地方,是闭包作为参数传入。
关于闭包的语法介绍

其实可以认为闭包是一个匿名函数(虽然实际上在swift中Functions are a special case of closures)
然后本身作为高阶函数,map就是接收一个处理函数。

所以在定义ptxMap时,会传入一个dealFunc的闭包。
因为题目是用reduce来实现map,所以在方法体里调用Array的reduce方法,而reduce的处理函数在这里只是简单做一个往原始数组里面添加元素的操作。

第二个生涩的地方应该是swift的具体语法了,如通过self来调用reduce,还有尾部闭包的语法,然后因为Array本身支持泛型,所以append操作的时候需要转类型。

这样,第一个版本的ptxMap就完成了,测试用例1也能满足。

version one:

extension Array {
    func ptxMap(_ dealFunc: (Int) -> Int) -> [Int] {
        return self.reduce([]) { (result, element) -> [Int] in
            var tempResult = result
            tempResult.append(dealFunc(element as! Int))
            return tempResult
        }
    }
}
//testcase1 succeed

3、添加测试用例,测试可能会失败

平时写代码的时候,虽然先写了一些测试场景,并且也通过了。
但是写着写着,可能就想到新的测试用例了。
testcase1,输出为[Int]类型,所以ptxMap的返回类型也被写死了。
所以这个时候会想到假如输出的类型是由处理闭包决定的呢?
比如下面的testcase2,闭包返回String,ptxMap的返回类型应该为[String],此时testcase2肯定不能通过测试,那就改代码呗。

testcase2:

let resultA = testArray.map() { "\($0)" }
let resultB = testArray.ptxMap() { "\($0)" }
assert(resultA == resultB, "two results should equal")
//["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
//testcase2 fail

4、改进程序

既然要满足不同类型的返回,那么有两个方法:
1、函数重载。函数名相同的情况下,参数类型不同。
2、泛型。
如果选择第一种方法,那么返回类型就是有限的,而选择第二种方法,则能支持 满足当前泛型的类型约束的 所有类型
所以第二个版本是支持泛型的版本。

在ptxMap函数名后声明一个即将要用到的泛型类型T,代码如下。

version two:

extension Array {
    public func ptxMap<T>(_ dealFunc: (Element) -> T) -> [T] {
        return self.reduce([]) { (result, element) -> [T] in
            var tempResult = result
            tempResult.append(dealFunc(element))
            return tempResult
        }
    }
}
//testcase1 succeed
//testcase2 succeed

这样,testcase2也满足了,其实这里的testcase并不完整,还应该添加一些边界的testcase,像空数组调用等。
好了,map的实现大概就这样了。

延伸,闭包的调用方式

在我们这个例子里,因为ptxMap的参数只有一个,并且为闭包,所以就使用下面这样方式调用,这也是比较建议的调用方式。
而后面这几种,则在这个例子中不太推荐。
其他情况参考raywenderlich swift-style-guide

Preferred:

testArray.ptxMap { $0 * 2 } 

Not Preferred:

testArray.ptxMap() { $0 * 2 } 
testArray.ptxMap({ (value) -> Int in
    return value * 2
})
testArray.ptxMap({ $0 * 2 })

Filter和完整代码

下面的代码实现了ptxFilter,因为filter并不涉及到输出类型的变化,所以并没有用到泛型。
下面是完整的代码,测试用例使用了do{}的方式,目的是隔离每个测试用例。

import Foundation

let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

extension Array {
    public func ptxMap<T>(_ dealFunc: (Element) -> T) -> [T] {
        return self.reduce([]) { (result, element) -> [T] in
            var tempResult = result
            tempResult.append(dealFunc(element))
            return tempResult
        }
    }
    
    public func ptxFilter(_ dealFunc: (Element) -> Bool) -> [Element] {
        return self.reduce([]) { (result, element) -> [Element] in
            var tempResult = result
            if dealFunc(element) {
                tempResult.append(element)
            }
            return tempResult
        }
    }
}

do {
    let resultA = testArray.map { $0 * 2 }
    let resultB = testArray.ptxMap { $0 * 2 }
    assert(resultA == resultB, "two results should equal")
    print(resultB)
}

do {
    let resultA = testArray.map { "\($0)" }
    let resultB = testArray.ptxMap { "\($0)" }
    assert(resultA == resultB, "two results should equal")
    print(resultB)
}

do {
    let resultA = testArray.filter { $0 % 2 == 0 }
    let resultB = testArray.ptxFilter { $0 % 2 == 0 }
    assert(resultA == resultB, "two results should equal")
    print(resultB)
}

总结

额,为啥要蛋疼写总结呢
首先,在这里栽过跟头,然后,以后所有的学习结果都会用总结的方式来梳理一遍。

最近还在刷算法和数据结构,额,有一些主题大学的时候并没有涉及到,这次也算还回来了,后面再梳理下。
其实解题的时候方法也挺重要的,先明确,然后思考当前的所有方案,选择复杂度相对好的方案,实现,然后一步步优化。
这跟写业务的过程区别不大,所以,有时候需要注重解决问题的方法,因为这不仅仅适用于当前的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容