如何处理闭包递归

时间:2014-08-03 09:54:16

标签: swift

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

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

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

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

我收到编译错误:“变量在其自己的初始值中使用”

5 个答案:

答案 0 :(得分:11)

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

var lap : (Int) -> Int!
lap = {
    (n: Int) -> Int in
    if n == 0 { return 0 }
    return lap(n - 1)
}

或者您可以使用Y combinator

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) }
}

这是@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")
    }
}

答案 1 :(得分: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) }

虽然目前的例子并不明显,但所谓的局部函数捕获了封闭范围的局部。

使用定位函数不会泄漏,而闭包会。但是,很明显,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)
}

不幸的是,因为explicit capture syntax无法应用于闭包类型(您会收到错误消息,说明&#34;&#39;无主&#39;无法应用于非类型&#39;( Int) - &gt; Int&#39;&#34;),似乎没有简单的方法来实现这一点而不会泄漏。我提交了一份关于它的错误报告。

答案 2 :(得分:3)

这是对我自己的问题的回应:

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

答案 3 :(得分:0)

这个怎么样:

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

这很简单,我刚刚在闭包中定义了一个递归的局部函数,它返回了函数。

但是,我不得不说@Bryan Chen关于 Y组合器的答案很棒。

答案 4 :(得分:0)

我遇到了同样的问题,对那里的任何内容都不满意,所以我创建了一个库,并在GitHub上提供了它。

使用这个库(使用Swift 3.0),您的代码将如下所示:

let lap = Recursion<Int, Int> { (n, f) in
    n == 0 ? 0 : f(n-1)
}.closure