【问题标题】:Why add "()" after closure body in Golang?为什么在 Golang 的闭包体后添加“()”?
【发布时间】:2013-04-07 04:18:08
【问题描述】:

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

Function literals:

func(ch chan int) { ch (replyChan)`

Defer statements的例子中:

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

我不清楚在闭包体之后添加和使用“()”的原因,希望有人能解释清楚。

【问题讨论】:

    标签: go closures


    【解决方案1】:

    并不是() 必须(仅)在defer 中的闭包 之后添加。 defer statement 的语言规范要求其“表达式”始终必须是函数调用。

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

    考虑:

    func f() int { return 42 }
    

    a := f
    

    b := f()
    

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

    以下的语义也是如此:

    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'的值 em> 执行。

    【讨论】:

    • 我明白你的意思,所以在func(ch chan int) { ch <- ACK }(replyChan)中,func(ch chan int) { ch <- ACK }表示闭包的定义,(replyChan)表示“使用参数replyChan执行这个闭包”。
    • 最后一个例子的另一个问题:in the moment when the closure executesin the moment when the defer statement was executed 之间有什么区别?如果我在for{} 之后有一个return,那么这两个defers 应该同时在return 之前执行,不是吗?
    • 我对此做了一些实验,但仍然对defer的执行感到困惑,你知道有什么文章可以解释这个吗?
    • @ReckHou:我相信规格(答案中的链接)很简洁,但是关于“延迟”声明的完整。我认为欢迎您就您不清楚的任何细节提出另一个 SO 问题。
    • 我是否正确理解 func(ch chan int) { ch <- ACK }(replyChan) chchan 是函数字面量的参数,如果是这样,为什么它们不用逗号分隔?谢谢?
    【解决方案2】:

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

    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】:

      参考文献

      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

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

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

      Function literals

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

      FunctionLit = FunctionType Body .
      

      函数字面量是闭包:它们可以引用定义在 一个周边功能。然后这些变量在 周围的函数和函数字面量,它们作为 只要它们可以访问。

      函数字面量可以分配给变量或直接调用。

      Calls

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

      f(a1, a2, … an)
      

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

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

      Defer statements

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

      DeferStmt = "defer" Expression .
      

      表达式必须是函数或方法调用。每次 “defer”语句执行,函数值和参数传给 call 像往常一样评估并重新保存,但实际功能是 没有被调用。相反,延迟调用按 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

      【讨论】:

        【解决方案4】:

        没有 (),你就没有执行函数。

        【讨论】:

        • 我感觉有人已经提到过。此外,您的声誉足以发表评论。
        猜你喜欢
        • 2013-05-30
        • 1970-01-01
        • 2014-10-08
        • 2020-02-17
        • 2012-03-12
        • 1970-01-01
        • 2012-12-27
        • 2013-06-29
        • 2019-06-23
        相关资源
        最近更新 更多