如何处理闭包递归

Luc*_*ier 11 swift

这是一个非常简单的递归函数:

func lap (n: Int) -> Int {
    if n == 0 { return 0 }
   return lap (n - 1)
}
Run Code Online (Sandbox Code Playgroud)

如果我想将其转换为闭包:

let lap = {
    (n: Int) -> Int in
    if n == 0 { return 0 }
    return lap (n - 1)
}
Run Code Online (Sandbox Code Playgroud)

我收到一个编译器错误:"变量在其自己的初始值中使用"

Bry*_*hen 11

您可以通过两步分配来解决它

var lap : (Int) -> Int!
lap = {
    (n: Int) -> Int in
    if n == 0 { return 0 }
    return lap(n - 1)
}
Run Code Online (Sandbox Code Playgroud)

或者你可以使用Y 组合器

func Y<T, R>( f: (T -> R) -> (T -> R) ) -> (T -> R) {
    return { t in f(Y(f))(t) }
}

let lap = Y {
    (f : Int -> Int) -> (Int -> Int) in
    return { (n : Int) -> Int in return n == 0 ? 0 : f(n - 1) }
}

// with type inference 
let lap2 = Y {
    f in { n in n == 0 ? 0 : f(n - 1) }
}
Run Code Online (Sandbox Code Playgroud)

这是@zneak发现的内存泄漏问题的解决方法 (它没有内存泄漏但是捕获了错误的值)

func f(n: Int) {
    var f = Foo()
    var lap: @objc_block (Int)->Int = { $0 }
    var obj: NSObject = reinterpretCast(lap)
    lap = {
        [weak obj] (n: Int) -> Int in // unowned will cause crush
        if n == 0 { return 0 }
        println(f)
        var lap2 : @objc_block (Int)->Int = reinterpretCast(obj)
        return lap2 (n - 1)
    }
    lap(n)
}

for i in 0..<5 {
    f(i)
}

class Foo {
    init() {
        println("init");
    }

    deinit {
        println("deinit")
    }
}
Run Code Online (Sandbox Code Playgroud)


zne*_*eak 10

编辑这已经使用嵌套函数使用Swift 2解决了.Apple建议使用以下代码:

func f(n: Int) {
    func lap(n: Int) -> Int {
        if n == 0 { return 0 }
        print(n)
        return lap(n - 1)
    }
    lap(n)
}

for i in 0..<1000000 { f(i) }
Run Code Online (Sandbox Code Playgroud)

虽然这在当前示例中并不明显,但所谓的局部函数捕获了封闭范围的局部变量.

使用位置函数不会泄漏,而封闭会发生泄漏.但是,显然,lap在这种情况下无法重新分配.

我收到了Apple的Joe Groff发来的一封电子邮件,声称他们仍然计划在稍后的时间内将闭包捕获为弱变量和可变变量.但这确实证实,除了本地功能之外,现在没有办法做到这一点.


您当前的解决方案中存在内存泄漏:lap封闭具有对自身的强烈引用,这意味着它无法释放.通过附带Leaks仪器启动以下程序可以轻松验证这一点:

import Foundation

func f(n: Int) {
    var lap: (Int)->Int = { $0 }
    lap = {
        (n: Int) -> Int in
        if n == 0 { return 0 }
        println(n)
        return lap (n - 1)
    }
    lap(n)
}

for i in 0..<1000000 {
    f(i)
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,由于显式捕获语法不能应用于闭包类型(你得到的错误说"'无主'不能应用于非类型'(Int) - > Int'"),似乎没有简单的方法实现这一点而不泄漏.我提交了一份关于它的错误报告.