【问题标题】:Why don't recursive swift function definitions create issues with their initial 'values'?为什么递归 swift 函数定义不会对其初始“值”产生问题?
【发布时间】:2020-12-08 02:29:56
【问题描述】:

这里有很多关于尝试在 Swift 中定义递归闭包的有趣问题。我认为答案最清楚的是这篇询问why one can't declare and define a recursive closure in the same line in Swift 问题的帖子。但是,他们不会问相反的问题。也就是说,如果我不能创建这样的代码:

var countdown = { i in
    print(i)
    if i > 1 {
        countdown(i - 1)
    }
}
// error: Variable used within its own initial value

那为什么我可以写这样的代码:

func countdown(_ i: Int) {
    print(i)
    if i > 1 {
        countdown(i - 1)
    }
}

函数有什么特别之处,使得它们在自己的声明中在尝试调用自己时遇到任何问题?我理解闭包背后的问题:它试图捕获一个尚不存在的值(闭包本身)。但是,我不明白为什么该功能没有同样的问题。查看Swift Book,我们看到:

全局函数是具有名称且不捕获任何值的闭包。

这是一个半答案:上面的函数不会引起问题,因为函数不捕获值。但是,如果函数不捕获值(尤其是递归值),它们如何工作?

【问题讨论】:

  • 为什么函数需要捕获值才能工作?
  • 因为您已将一个声明为属性,另一个声明为函数。

标签: swift function recursion closures


【解决方案1】:

不太确定您在寻找什么答案。我的意思是,一个简单的答案是 Swift 编译器就是这样工作的。

一个原因可能是func 表达式,例如func countdown(...) {},编译器可以放心地假定函数将在它调用自身时定义。

而对于变量定义(即变量获取其值的表达式),它通常不会做出这样的假设,因为类似

var c = c + 1

由于相同的“变量在其自己的初始值中使用”错误,显然无法正常工作。

话虽如此,编译器可能已经对闭包类型变量的定义进行了特殊处理,因为与非闭包变量不同,在定义它们时不需要它们的实际值。

这就是为什么一个解决方案是定义变量(或者至少重新向编译器保证它将使用! 定义),这样编译器就不会抱怨:

var countdown: ((Int) -> Void)!
countdown = { i in
    print(i)
    if i > 1 {
        countdown(i - 1)
    }
}

【讨论】:

  • 聪明的解决方法,但重要的是要注意,在此示例中,我们已将 countdown 从存储属性更改为函数(或闭包),这就是编译器不会抱怨的原因。跨度>
  • @bsod,你说的存储属性是什么意思?我认为这只是一般性陈述,例如print(5);不是对象的属性(计算或存储)。
  • 我的意思是我在 OP 的示例中理解 var countdown = {...} 是指一个语句 - 不是对象 @bsod 的属性
  • @bsod,也许我们不了解对方。我的意思是:do { let fn = { $0 + 1 }; print(fn(5)) }...在这里,let fn = {...} 是一个赋值语句,其中fn 是一个(Int) -> Int 类型的变量,被赋值...与OP 的var countdown = {...} 示例相同
  • 你可能的意思是(假设)这是一个struct Foo { var countdown = {...} } - 可能,当然,但我不是这么理解的
【解决方案2】:

一个是函数,另一个是存储属性,从语义上讲,存储属性(和属性)是状态,函数是动作。这就是 Swift 编译器如何解释您的代码以及为什么它不起作用的原因。

在您的示例中,countdown 被编译器编译为存储属性。您可以将存储的属性转换为计算属性,使其表现得更像一个函数,但您不仅会失去传递参数的能力,而且该属性仍然无法在自己的 getter 中访问自己(因为它仍然属性而不是函数)。

New Dev 的答案是语法变通方法,它将countdown 从存储属性更改为函数——他将其声明为函数,然后为其分配了一个闭包。你试图将一个函数塞进一个属性中,并想知道为什么 Swift 对其中一个进行了特殊处理——它没有,你只是将它们变成了两种不同的类型。我特别指出这一点,因为您的问题(在我看来)是为什么函数与属性的处理方式不同。这是因为函数和属性本质上是两个不同的东西。

/* This syntax tells the compiler that
   countdown is a stored property, hence
   the error of accessing self in the
   property's own getter. */
var countdown = { i in
    print(i)
    if i > 1 {
        countdown(i - 1)
    }
}

/* This syntax tells the compiler that
   countdown is a computed property, but
   this will still not compile because
   computed properties cannot take arguments
   or access self in the getter. */
var countdown: (Int) -> () { i in
    print(i)
    if i > 1 {
        countdown(i - 1)
    }
}

/* All this does is tell the compiler
   that countdown is of type function.
   This variable will no longer be compiled
   as a stored property. We've changed its
   type to function which is why it works. */
var countdown: ((Int) -> Void)!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 2017-02-26
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    相关资源
    最近更新 更多