为什么在Golang中闭包体后面要加上”()”?

huangapple go评论69阅读模式
英文:

Why add "()" after closure body in Golang?

问题

我正在阅读《Go编程语言规范》,发现自己对闭包体后面的“()”并不真正理解:

在“函数字面量”中:

func(ch chan int) { ch <- ACK }(replyChan)`

在“延迟语句”的示例中:

// f返回1
func f() (result int) {
    defer func() {
        result++
    }() // 为什么要这样做?如何解释?
    return 0
}

我不清楚为什么要在闭包体后面添加“()”以及使用“&”的原因,希望有人能清楚地解释一下。

英文:

I'm reading The Go Programming Language Specifications and found myself not truly understand with "()" after closure body:

In Function literals:

>func(ch chan int) { ch <- ACK }(replyChan)`

In Defer statements's example:

// f returns 1
func f() (result int) {
    defer func() {
	    result++
    }() // why and how?
	return 0
}

I'm not clear about the reason to add & usage of "()" after closure body, hope someone can explain this clearly.

答案1

得分: 81

不是在defer中的闭包后面必须添加()defer语句的语言规范要求它的'Expression'必须始终是一个函数调用。

为什么会这样呢?这与任何其他函数一样:

考虑以下代码:

func f() int { return 42 }

a := f

b := f()

第一个表达式的右侧是一个函数值。在第二个版本中,右侧是函数返回的值 - 即函数调用。

所以,语义上是这样的:

defer f

defer f()

只是第一个版本在'defer'的上下文中没有意义,因此规范中提到它必须是第二种形式(仅限于此)。

我认为这样学习起来也更容易,因为它与上面讨论的在'defer'语句之外的函数调用具有正交性。

还要注意,函数调用不仅仅是fn-expr后面跟着(),而是通常在括号内包含一个表达式列表(包括空列表)。以下两个示例之间有很大的区别:

for i := range whatever {
        defer func() { fmt. Println(i) }()
}

for i := range whatever {
        defer func(n int) { fmt. Println(n) }(i)
}

第一个版本在闭包执行时打印'i'的值,第二个版本在defer语句执行时打印'i'的值。

英文:

It's not that () must be added after (only) a closure in defer. The language specs for the defer statement mandate that its 'Expression' always must be a function call.

And why is it so? It's the same as with any other function, in 'defer' or not:

Consider:

func f() int { return 42 }

and

a := f

vs

b := f()

The first expression RHS is a function value. In the second version the RHS is the value returned by the function - i.e. a function call.

So is the semantics of:

defer f

vs

defer f()

except that the first version doesn't make sense in the context of 'defer', and so the specifications mention that it must be the second form (only).

It's IMHO also easier to learn because of the orthogonality with the above discussed function call outside of the 'defer' statement.

Also note that a function call is not only fn-expr followed by (), but an expression list is generally inside the parenthesis (including an empty list). There's a big difference between:

for i := range whatever {
        defer func() { fmt. Println(i) }()
}

and

for i := range whatever {
        defer func(n int) { fmt. Println(n) }(i)
}

The first version prints the value of 'i' in the moment when the closure executes, the second prints the value of 'i' in the moment when the defer statement was executed.

答案2

得分: 21

如果你不想阅读长篇回答:

str := "Alice"
go func(name string) {
	fmt.Println("你的名字是", name)
}(str)

与以下代码相同:

str := "Alice"
f := func(name string) {
	fmt.Println("你的名字是", name)
}
go f(str)
英文:

If you don't want to read long answers:

str := &quot;Alice&quot;
go func(name string) {
	fmt.Println(&quot;Your name is&quot;, name)
}(str)

Is same as:

str := &quot;Alice&quot;
f := func(name string) {
	fmt.Println(&quot;Your name is&quot;, name)
}
go f(str)

答案3

得分: 16

> 参考资料
>
> Go编程语言规范
>
> 函数类型
>
> 函数类型表示具有相同参数和结果类型的所有函数的集合。
>
> FunctionType = "func" Signature .
> Signature = Parameters [ Result ] .
> Result = Parameters | Type .
> Parameters = "(" [ ParameterList [ "," ] ] ")" .
> ParameterList = ParameterDecl { "," ParameterDecl } .
> ParameterDecl = [ IdentifierList ] [ "..." ] Type .
>
> 函数声明
>
> 函数声明将标识符(函数名)绑定到一个函数。
>
> FunctionDecl = "func" FunctionName Signature [ Body ] .
> FunctionName = identifier .
> Body = Block .
>
> 函数字面量
>
> 函数字面量表示一个匿名函数。它由函数类型的规范和函数体组成。
>
> FunctionLit = FunctionType Body .
>
> 函数字面量是闭包:它们可以引用在周围函数中定义的变量。这些变量在周围函数和函数字面量之间共享,并且只要它们可访问,它们就会存在。
>
> 函数字面量可以赋值给一个变量或直接调用。
>
> 函数调用
>
> 给定一个函数类型为F的表达式f
>
> f(a1, a2, … an)
>
> 调用f并传入参数a1, a2, … an
>
> 在函数调用中,函数值和参数按照通常的顺序进行求值。在它们求值之后,调用的参数按值传递给函数,并且被调用的函数开始执行。函数的返回参数在函数返回时按值传递回调用函数。
>
> 延迟语句
>
> “defer”语句会调用一个在周围函数返回时被延迟执行的函数。
>
> DeferStmt = "defer" Expression .
>
> 表达式必须是一个函数或方法调用。每次执行“defer”语句时,函数值和调用的参数按照通常的方式进行求值并保存,但实际的函数不会被调用。相反,延迟调用会在周围函数返回之前以LIFO顺序立即执行,返回值(如果有)在它们被返回给调用者之前被求值。
>
> 延迟语句的表达式必须是一个直接调用的函数或方法调用,而不仅仅是一个未直接调用的函数或方法字面量。因此,函数或方法字面量需要跟随“()`函数调用运算符,以使延迟语句的表达式成为一个函数或方法调用。
>
> 以下延迟语句是有效的:
>
> defer func(i int) int { return 42 * i }(7)
>
> 以下延迟语句是无效的:
>
> defer func(i int) int { return 42 * i }
>
> 错误信息为:“syntax error: argument to go/defer must be function call”。
>
> 1: http://golang.org/ref/spec
> 2: http://golang.org/ref/spec#Function_types
> 3: http://golang.org/ref/spec#Function_declarations
> 4: http://golang.org/ref/spec#Function_literals
> 5: http://golang.org/ref/spec#Calls
> 6: http://golang.org/ref/spec#Defer_statements

英文:

> References
>
> The Go Programming Language Specification
>
> Function types
>
> A function type denotes the set of all functions with the same
> parameter and result types.
>
> FunctionType = "func" Signature .
> Signature = Parameters [ Result ] .
> Result = Parameters | Type .
> Parameters = "(" [ ParameterList [ "," ] ] ")" .
> ParameterList = ParameterDecl { "," ParameterDecl } .
> ParameterDecl = [ IdentifierList ] [ "..." ] Type .
>
> Function declarations
>
> A function declaration binds an identifier, the function name, to a
> function.
>
> FunctionDecl = "func" FunctionName Signature [ Body ] .
> FunctionName = identifier .
> Body = Block .
>
> Function literals
>
> A function literal represents an anonymous function. It consists of a
> specification of the function type and a function body.
>
> FunctionLit = FunctionType Body .
>
> Function literals are closures: they may refer to variables defined in
> a surrounding function. Those variables are then shared between the
> surrounding function and the function literal, and they survive as
> long as they are accessible.
>
> A function literal can be assigned to a variable or invoked directly.
>
> Calls
>
> Given an expression f of function type F,
>
> f(a1, a2, … an)
>
> calls f with arguments a1, a2, … an.
>
> In a function call, the function value and arguments are evaluated in
> the usual order. After they are evaluated, the parameters of the call
> are passed by value to the function and the called function begins
> execution. The return parameters of the function are passed by value
> back to the calling function when the function returns.
>
> Defer statements
>
> A "defer" statement invokes a function whose execution is deferred
> to the moment the surrounding function returns.
>
> DeferStmt = "defer" Expression .
>
> The expression must be a function or method call. Each time the
> "defer" statement executes, the function value and parameters to the
> call are evaluated as usual and saved anew but the actual function is
> not invoked. Instead, deferred calls are executed in LIFO order
> immediately before the surrounding function returns, after the return
> values, if any, have been evaluated, but before they are returned to
> the caller.

Since you are still confused, here's another attempt to provide an answer to your question.

In the context of your question, () is the function invocation operator.

For example, the function literal

func(i int) int { return 42 * i }

represents an anonymous function.

The function literal followed by the () function invocation operator

func(i int) int { return 42 * i }(7)

represents an anonymous function which is then invoked directly.

Ordinarily, in a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.

However, invoking the function through the defer statement is a special case. Each time the "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred calls are executed in LIFO order immediately before the surrounding function returns, after the return values, if any, have been evaluated, but before they are returned to the caller.

The defer statement expression must be a function or method call that is invoked directly, not just a function or method literal which is not invoked directly. Therefore, the function or method literal needs to be followed by the () function invocation operator so that the defer statement expression is a function or method call.

The defer statement

defer func(i int) int { return 42 * i }(7)

is valid.

The defer statement

defer func(i int) int { return 42 * i }

is invalid: syntax error: argument to go/defer must be function call.

答案4

得分: 1

没有括号,你就不能执行这个函数。

英文:

Without the (), you are not executing the function.

huangapple
  • 本文由 发表于 2013年4月15日 14:06:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/16008604.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定