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

Rec*_*Hou 42 closures go

我正在阅读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
}
Run Code Online (Sandbox Code Playgroud)

我不清楚在封闭体之后添加和使用"()"的原因,希望有人能够清楚地解释这一点.

zzz*_*zzz 64

这并不是说()必须(仅)一个后添加defer.延迟语句的语言规范要求其"表达式" 始终必须是函数调用.

为什么会这样呢?它与"延迟"中的任何其他功能相同:

考虑:

func f() int { return 42 }
Run Code Online (Sandbox Code Playgroud)

a := f
Run Code Online (Sandbox Code Playgroud)

VS

b := f()
Run Code Online (Sandbox Code Playgroud)

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

语义也是如此:

defer f
Run Code Online (Sandbox Code Playgroud)

VS

defer f()
Run Code Online (Sandbox Code Playgroud)

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

由于与"延迟"语句之外的上述函数调用的正交性,恕我直言也更容易学习.

另请注意,函数调用不仅是fn-expr后跟(),而且表达式列表通常位于括号内(包括空列表).两者之间有很大的不同:

for i := range whatever {
        defer func() { fmt. Println(i) }()
}
Run Code Online (Sandbox Code Playgroud)

for i := range whatever {
        defer func(n int) { fmt. Println(n) }(i)
}
Run Code Online (Sandbox Code Playgroud)

第一个版本打印"我"在当下的价值关闭时执行,第二打印的延迟语句时,"我"在当下的价值执行.


pet*_*rSO 16

参考

Go编程语言规范

功能类型

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

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

函数声明

函数声明将标识符(函数名称)绑定到函数.

FunctionDecl = "func" FunctionName Signature [ Body ] .
FunctionName = identifier .
Body         = Block .
Run Code Online (Sandbox Code Playgroud)

函数文字

函数文字表示匿名函数.它由函数类型的规范和函数体组成.

FunctionLit = FunctionType Body .
Run Code Online (Sandbox Code Playgroud)

函数文字是闭包:它们可以引用周围函数中定义的变量.然后,这些变量在周围函数和函数文本之间共享,只要它们可访问,它们就会存在.

函数文字可以分配给变量或直接调用.

呼叫

给出f函数类型的表达式F,

f(a1, a2, … an)
Run Code Online (Sandbox Code Playgroud)

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

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

推迟发言

" defer"语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻.

DeferStmt = "defer" Expression .
Run Code Online (Sandbox Code Playgroud)

表达式必须是函数或方法调用.每次defer执行" "语句时,都会照常调用函数值和调用参数并重新保存,但不会调用实际函数.相反,延迟调用在周围函数返回之前立即以LIFO顺序执行,在返回值(如果有)已经评估之后,但在它们返回给调用者之前.

既然你仍然感到困惑,那么这是另一种尝试来回答你的问题.

在您的问题的上下文中,()是函数调用操作符.

例如,函数文字

func(i int) int { return 42 * i }
Run Code Online (Sandbox Code Playgroud)

代表一个匿名函数.

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

func(i int) int { return 42 * i }(7)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

推迟陈述

defer func(i int) int { return 42 * i }(7)
Run Code Online (Sandbox Code Playgroud)

已验证.

推迟陈述

defer func(i int) int { return 42 * i }
Run Code Online (Sandbox Code Playgroud)

无效:syntax error: argument to go/defer must be function call.


Eri*_*kas 16

如果您不想阅读长答案:

str := "Alice"
go func(name string) {
    fmt.Println("Your name is", name)
}(str)
Run Code Online (Sandbox Code Playgroud)

等同于:

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