ARIS - 2

242 阅读3分钟

A


377. 组合总和 Ⅳ

给定一个由正整数组成且不存在重复数字的数组 nums ,找出和为给定目标正整数 target 的组合的个数。

动态规划法

建立 dp[target+1] 数组,设 nums 的长度为 N 。其中 dp[0]=1

dp[i]=\sum^{N}_{n=0}{((target-n>0)?dp[target-n]:0)}

dp[1] 开始推导,最后的 dp[target] 即为期望结果。

回溯法

target 开始往前推算,同样是遍历 nums ,但只计算 target-n 的值,每次计算时调用函数递归计算,直到 0 时再往上合并。

为了避免重复计算 target-n 的值,可以用一个 map 将计算过的值保存起来。

两种方法的对比

动态规划法只需要调一个函数,无需创建大量对象,即可计算出结果。但是会计算出一些不需要计算的 i!=target-n 的值。

回溯法在配上 map 后,可以最小化计算的 dp 数,只计算必需会用到的值。理论上比动态规划法更好,但是实际运行中,除去函数的嵌套调用层级容易过深,不利于 debug 外,多次的嵌套调用会创建很多对象,实际用时通常会比动态规划法更慢。

R


Avoiding Callback Hell in Swift

文章介绍几种避免陷入回调地狱的方法。

Promises

这是一个第三方库,通过使用其包含的 then(completion:)catch(completion:) 方法来简化层级,参考示例如下:

func perform<T: Request>(request: T) -> Promise<T.Response>
    return Promise { promise in
       //Do the actual request here, then:
       promise.fulfill(response)
    }
}
perform(requestOne()).then { responseOne in 
    perform(requestTwo(responseOne: responseOne))
}.then { responseTwo in
    perform(requestThree(responseTwo: responseTwo))
}.then { responseThree in
    perform(requestFour(responseThree: responseThree))
}.catch { error in
    print(error)
}.always {
    print("Finished")
}

它可以有效避免嵌套的回调地狱出现,但是需要引用第三方库。

OperationQueue

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
func doLotsOfStuff(completion: (() -> Void)?) {
    let firstOperation: Operation = FirstOperation()
    let secondOperation: Operation = SecondOperation()
    secondOperation.completionBlock = {
      completion?()
    }
    secondOperation.addDependency(firstOperation)
    queue.addOperation(firstOperation)
    queue.addOperation(secondOperation)
}

Foundation 里自带的方法,可以在一定程度上减少回调层级,但是如果 queue 里有网络请求,或者 operation 依赖于前一个的返回值,就无法使用了。

虽然可以通过预先创建 data 对象,作为参数传入给每个 operation 或在自定义的 operation 类创建数据对象,在调用时从 dependencies 里查找,但是并不是一种好的实现。

高阶函数

let sum = array.reduce(0, +)
//reduce() here is an ((Int, ((Int, Int) -> Int)) -> Int)
//and the + operator is func +(lhs: Int, rhs: Int) -> Int,
//... or ((Int, Int) -> Int), so there's no need to define reduce's closure.

将方法对象做为闭包参数传递,可以减少方法中嵌套闭包的数量。但是要求先创建闭包对象,不便于阅读。

T


Swift 闭包可以通过 $0$1 等来表示闭包中的第1,2 个参数,使用时其对应的参数类型也会根据函数类型判断。

let numbers = [1,2,5,4,3,6,8,7]
let sortNumbers = numbers.sorted(by: { (a, b) -> Bool in
    return a < b
})
let numbers = [1,2,5,4,3,6,8,7]
let sortNumbers = numbers.sorted(by: {$0 < $1})

S


coroutineScope , supervisorScope 和 launch ,async 的混合异常处理