【问题标题】:How do I create an _inline_ recursive closure in Swift? [duplicate]如何在 Swift 中创建 _inline_ 递归闭包? [复制]
【发布时间】:2015-08-11 22:46:01
【问题描述】:

递归对于 Swift 中的全局函数来说是微不足道的。例如:

func f()
{
    f()
}

但是,闭包不能引用自身。例如:

var f: (Void -> Void) =
{
    f()
}

产生以下错误:

Variable used within its own initial value

有解决办法吗?如何创建递归闭包内联

【问题讨论】:

    标签: function swift recursion functional-programming


    【解决方案1】:

    限制是两个对象不能同时实例化,不能互相引用。一个必须在另一个之前被创建。您可以将函数隐式解包标记为可选。这样你就可以用nil 初始化函数,但是“承诺”它稍后会有一个值。

    var f: (Void -> Void)!
    
    f = {
        f()
    }
    

    更新: 另一种没有隐式展开选项的方法:

    var f: (Void -> Void)
    
    var placeholder: (Void -> Void) = {
        f()
    }
    
    f = placeholder
    

    【讨论】:

    • 虽然这行得通,但它很丑陋,需要我使用可选项,我宁愿不这样做。
    • 我相信没有其他选择。 @IBOutlet 使用相同的方法。我相信这是隐式展开的选项在语言中的主要原因。
    • 看看我自己的回答。
    • 他们承诺 Swift 中不会有头文件,但这与 C 中的 .h 头文件有什么不同?
    • 更新方法报如下错误:`variable 'f' 在被初始化之前被闭包捕获
    【解决方案2】:

    有一个解决方法:

    func unimplemented<T>() -> T
    {
        fatalError()
    }
    
    func recursive<T, U>(f: (@escaping (((T) -> U), T) -> U)) -> ((T) -> U)
    {
        var g: ((T) -> U) = { _ in unimplemented() }
    
        g = { f(g, $0) }
    
        return g
    }
    

    recursive 是一个接受闭包(((T) -&gt; U), T) -&gt; U 的函数,其中((T) -&gt; U) 是对闭包的剥离版本的引用,并返回一个可用函数g

    g 最初被分配了一个假函数(调用时崩溃)。这样做是为了启用新值g 的递归,其中gT 的输入值一起传递给f。需要注意的是,g = { f(g, $0) } 中的 g 指的是它自己,而不是之前分配给它的假函数。因此,每当((T) -&gt; U) 参数在f 中被引用时,它就是对g 的引用,而g 又引用了它自己。

    此函数允许内联递归,如下所示:

    recursive { f, x in x != 10 ? f(x + 1) : "success" }(0)
    

    此函数一共重复11次,无需声明单个变量。

    更新:现在适用于 Swift 3 preview 6!


    就个人而言,我觉得这是一个相当优雅的解决方案,因为我觉得它可以将我的代码简化到最低限度。一种 Y 组合器方法,例如下面的一种

    func recursive<T, U>(_ f: (@escaping (@escaping (T) -> U) -> ((T) -> U))) -> ((T) -> U)
    {
        return { x in return f(recursive(f))(x) }
    }
    

    会让我返回一个函数,一个转义闭包中的转义闭包!

    recursive { f in { x in x != 10 ? f(x + 1) : "success" } }(0)
    

    如果不是内部@escaping 属性,上面的代码将无效。它还需要另一组大括号,这使得它看起来比我在编写内联代码时所习惯的更冗长。

    【讨论】:

    • 这怎么比上面 Kirsteins 的回答好?您刚刚添加了更多样板,并且假设您的递归函数采用(T -&gt; U) 的形式(因此,如果不是这种情况,您必须使用类型系统进行特技飞行)。虽然不是一个理想的解决方案,但出于这个特定原因,隐式展开的可选项被引入到语言中。
    • @SashaChedygov:如果您查看时间戳,您会发现这是该问题的第一个答案。我的目的是以 Q/A 的形式分享我的知识。
    • 很公平,我看错了时间戳。
    • @SashaChedygov:至于我的解决方案,我的目标是严格的内联递归。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2018-09-09
    • 2010-10-03
    • 2020-04-18
    • 2022-01-12
    • 1970-01-01
    • 2014-09-26
    相关资源
    最近更新 更多