为什么在Golang中关闭体后添加“()”?

时间:2013-04-15 06:06:14

标签: go closures

我正在阅读The Go Programming Language Specifications并发现自己并非真正理解封闭体后的“()”:

Function literals

  

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

Defer statements的例子中:

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

我不清楚添加&的原因封闭体后使用“()”,希望有人能清楚地解释清楚。

4 个答案:

答案 0 :(得分:62)

()中的{em>闭包后,defer必须添加func f() int { return 42 } defer statement要求其“表达式”始终的语言规范必须是函数调用。

为什么会这样?它与任何其他功能相同,无论是否“延迟”:

考虑:

a := f

b := f()

VS

defer f

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

语义也是如此:

defer f()

VS

()

除了第一个版本在'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)
}

{{1}}

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

答案 1 :(得分:14)

  

参考

     

The Go Programming Language Specification

     

Function types

     

函数类型表示具有相同功能的所有函数的集合   参数和结果类型。

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
     

Function declarations

     

函数声明将标识符(函数名称)绑定到a   功能

FunctionDecl = "func" FunctionName Signature [ Body ] .
FunctionName = identifier .
Body         = Block .
     

Function literals

     

函数文字表示匿名函数。它由一个   函数类型和函数体的规范。

FunctionLit = FunctionType Body .
     

函数文字是闭包:它们可以引用在中定义的变量   周围的功能。然后在这些变量之间共享这些变量   周围函数和函数文字,它们存活下来   只要他们可以访问。

     

可以将函数文本分配给变量或直接调用。

     

Calls

     

给定函数类型f的表达式F

f(a1, a2, … an)
     

使用参数f调用a1, a2, … an

     

在函数调用中,将计算函数值和参数   通常的顺序。在评估它们之后,调用的参数   通过值传递给函数,并且被调用的函数开始   执行。函数的返回参数按值传递   函数返回时返回调用函数。

     

Defer statements

     

defer”语句调用其执行延迟的函数   到周围的函数返回的那一刻。

DeferStmt = "defer" Expression .
     

表达式必须是函数或方法调用。每一次   “defer”语句执行,函数值和参数   呼叫按常规进行评估并重新保存,但实际功能是   没有被调用。相反,延迟调用以LIFO顺序执行   在周围函数返回之前,返回之后   值(如果有)已经过评估,但在它们返回之前   来电者。

由于您仍然感到困惑,这是另一种尝试来回答您的问题。

在您的问题的上下文中,()是函数调用运算符。

例如,函数文字

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

表示匿名函数。

函数文字后跟()函数调用操作符

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

表示一个匿名函数,然后直接调用。

通常,在函数调用中,函数值和参数按通常顺序计算。在评估它们之后,调用的参数通过值传递给函数,并且被调用的函数开始执行。当函数返回时,函数的返回参数通过值传递回调用函数。

但是,通过defer语句调用该函数是一种特殊情况。每次执行“defer”语句时,将像往常一样评估调用的函数值和参数,并重新保存但不调用实际函数。相反,延迟调用在周围函数返回之前立即以LIFO顺序执行,在返回值(如果有)已经评估之后,但在它们返回给调用者之前。

defer语句表达式必须是直接调用的函数或方法调用,而不仅仅是不直接调用的函数或方法文字。因此,函数或方法文字需要由()函数调用操作符跟随,以便defer语句表达式是函数或方法调用。

延期陈述

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

答案 2 :(得分:0)

如果您不想阅读冗长的答案:

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

与:

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

答案 3 :(得分:0)

没有(),您没有执行该函数。