为什么递归 swift 函数定义不会为其初始 'values' 创建问题?
Why don't recursive swift function definitions create issues with their initial 'values'?
这里有多个关于尝试在 Swift 中定义递归闭包的有趣问题。我认为回答最清楚的是这个 post 问 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,我们看到:
Global functions are closures that have a name and don’t capture any values.
这是半个答案:上面的函数不会导致问题,因为函数不捕获值。但是,如果函数不捕获值(尤其是递归值),它们将如何工作?
一个是函数,另一个是存储的 属性,从语义上讲,存储的属性(和属性)是状态,函数是动作。这就是 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)!
不太确定您要寻找的答案是什么。我的意思是,一个简单的答案是 Swift 编译器就是这样工作的。
一个原因可能是 func
表达式,例如func countdown(...) {}
,编译器可以安全地假定该函数将在它调用自身时定义。
而对于变量定义(即变量获取其值的表达式),它通常不会做出这样的假设,因为像
var c = c + 1
由于相同的“在其自身初始值内使用的变量”错误,显然无法正常工作。
话虽如此,编译器可能会对闭包类型变量的定义进行特殊处理,因为与非闭包变量不同,它们在定义时不需要实际值。
这就是为什么一个解决方案是定义变量(或者至少向编译器保证它将用 !
定义),这样编译器就不会抱怨:
var countdown: ((Int) -> Void)!
countdown = { i in
print(i)
if i > 1 {
countdown(i - 1)
}
}
这里有多个关于尝试在 Swift 中定义递归闭包的有趣问题。我认为回答最清楚的是这个 post 问 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,我们看到:
Global functions are closures that have a name and don’t capture any values.
这是半个答案:上面的函数不会导致问题,因为函数不捕获值。但是,如果函数不捕获值(尤其是递归值),它们将如何工作?
一个是函数,另一个是存储的 属性,从语义上讲,存储的属性(和属性)是状态,函数是动作。这就是 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)!
不太确定您要寻找的答案是什么。我的意思是,一个简单的答案是 Swift 编译器就是这样工作的。
一个原因可能是 func
表达式,例如func countdown(...) {}
,编译器可以安全地假定该函数将在它调用自身时定义。
而对于变量定义(即变量获取其值的表达式),它通常不会做出这样的假设,因为像
var c = c + 1
由于相同的“在其自身初始值内使用的变量”错误,显然无法正常工作。
话虽如此,编译器可能会对闭包类型变量的定义进行特殊处理,因为与非闭包变量不同,它们在定义时不需要实际值。
这就是为什么一个解决方案是定义变量(或者至少向编译器保证它将用 !
定义),这样编译器就不会抱怨:
var countdown: ((Int) -> Void)!
countdown = { i in
print(i)
if i > 1 {
countdown(i - 1)
}
}