为什么递归swift函数定义的初始“值”不会产生问题?

为什么递归swift函数定义的初始“值”不会产生问题?,swift,function,recursion,closures,Swift,Function,Recursion,Closures,关于在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) {

关于在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)
    }
}
那么为什么我可以写这样的代码:

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编译器解释代码的方式,以及为什么它不工作

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

新开发人员的答案是一种语法解决方法,它将存储属性的倒计时更改为一个函数。他将其声明为一个函数,然后为其分配了一个闭包。你试图把一个函数塞进一个属性,想知道为什么斯威夫特对其中一个给予了特殊的处理,而对另一个却没有,你只是把它们做成了两种不同的类型。我特别指出这一点,因为在我看来,你们的问题是,为什么函数和属性被区别对待。这是因为函数和属性本质上是两个不同的东西

/* 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编译器解释代码的方式,以及为什么它不工作

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

新开发人员的答案是一种语法解决方法,它将存储属性的倒计时更改为一个函数。他将其声明为一个函数,然后为其分配了一个闭包。你试图把一个函数塞进一个属性,想知道为什么斯威夫特对其中一个给予了特殊的处理,而对另一个却没有,你只是把它们做成了两种不同的类型。我特别指出这一点,因为在我看来,你们的问题是,为什么函数和属性被区别对待。这是因为函数和属性本质上是两个不同的东西

/* 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倒计时。。。{},编译器可以安全地假定函数将在调用自身时定义

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

var c = c + 1
显然,由于在其初始值错误中使用了相同的变量,因此无法工作

也就是说,编译器可能对闭包类型变量的定义进行了特殊处理,因为与非闭包变量不同,在定义闭包类型变量时不需要它们的实际值

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

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

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

一个原因可能是使用func表达式,例如func倒计时。。。{},编译器可以安全地假定函数将在调用自身时定义

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

var c = c + 1
显然,由于在其初始值错误中使用了相同的变量,因此无法工作

也就是说,编译器可能可以 对闭包类型变量的定义进行了特殊处理,因为与非闭包变量不同,在定义闭包类型变量时不需要它们的实际值

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

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

为什么函数需要捕获值来执行工作?因为您已将一个声明为属性,另一个声明为函数。为什么函数需要捕获值来执行工作?因为您已将一个声明为属性,另一个声明为函数。这是一种巧妙的解决方法,但在此示例中需要注意的是,我们已经将倒计时从存储属性更改为函数或闭包,这就是为什么编译器没有抱怨。@bsod,存储属性是什么意思?我认为这只是一个一般性的陈述,就像print5一样;不是对象的计算或存储属性。我的意思是,我理解OP示例中的var countdown={…}是一条语句,而不是对象的属性@bsod@bsod,也许我们彼此不了解。我的意思是:{let fn={$0+1};printfn5}。。。这里,让fn={…}是一个赋值语句,其中fn是一个Int->Int类型的变量,被赋值。。。与OP的var countdown={…}示例相同,您的意思可能是假设这是一个结构Foo{var countdown={…}}-可能,当然,但这不是我理解它的方式聪明的解决方法,但需要注意的是,在这个示例中,我们将countdown从存储属性更改为函数或闭包,这就是编译器不抱怨的原因。@bsod,存储属性是什么意思?我认为这只是一个一般性的陈述,就像print5一样;不是对象的计算或存储属性。我的意思是,我理解OP示例中的var countdown={…}是一条语句,而不是对象的属性@bsod@bsod,也许我们彼此不了解。我的意思是:{let fn={$0+1};printfn5}。。。这里,让fn={…}是一个赋值语句,其中fn是一个Int->Int类型的变量,被赋值。。。与OP的var countdown={…}示例相同,您可能是指假设这是一个结构Foo{var countdown={…},当然可能,但我不是这样理解的