en 非公開: Closures in Go – Anonymous functions that reference external variables

Closures in Go – Anonymous functions that reference external variables

A closure in Go is a function that references a free variable. The referenced free variable exists with the function. It is not released or deleted when the free variable’s environment leaves. The free variable can still be used. Therefore, it is simple Type the following:

Function types are like structs and can be instantiated Functions themselves do not store any information Only the closure formed after combining with the reference environment has “memory” Functions are stored at compile time Closures are a static concept, and closures are runtime dynamics.

Closures in other programming languages

Closures are also known as lambda expressions in some programming languages.

The process of referencing a closure to a variable in the environment is sometimes called “capturing.” There are two types of captures in the C++11 standard: references and copies. The original value from which a reference can be modified is called a “reference capture.” The value of the procedure is copied into the closure using something called “copy capture.”

In the Lua language, the captured variable is named Upvalue. This is because the capture process always references free variables defined above closures.

The implementation of closures also differs depending on the language, but in the Lua language, both closures and functions belong to the concept of prototype, and captured variables are referenced to closures in the form of Upvalue.

In C++ and C#, classes are created for closures, and captured variables are placed into members of the class at compile time. When a closure accesses a captured variable, it actually accesses a member of the closure’s hidden class.

Modify a reference variable inside a closure

Closures can modify variables at the top of their scope, and modifying the referenced variable actually modifies the variable. This can be understood from the following example.

 // 文字列を準備する
str := "hello world"

// 無名関数を作成する
foo := func() {

    // 無名関数内でstrにアクセスする
    str = "hello dude"
}

// 無名関数を呼び出す
foo() 

The code description is as follows:

  • The second line prepares the string to be modified.
  • Line 5 creates an anonymous function.
  • In line 8, str is not defined within the anonymous function, but str is defined before the anonymous function, and at this time str is referenced to the anonymous function to form a closure.
  • Line 12 executes the closure, at which point str is changed to hello dude.

Code output:

hello dude

Example: Memory effect due to closure

Variables captured in a closure have a memory effect on the closure itself. Logic within a closure can modify variables captured by the closure. Variables always exist during the lifetime of a closure. Closures themselves have memory, just like variables. effect.

Accumulator implementation:

 import main

import (
    "fmt"
)

//値を提供して、関数を呼び出すたびに値を累算するようにしましょう
func Accumulate(value int) func() int {

    //クロージャーを返す
    return func() int {

        //累算する
        value++

        //累算された値を返す
        return value
    }
}

func main() {

    //1が初期値の累算器を作成する
    accumulator := Accumulate(1)

    //1を累算して印刷
    fmt.Println(accumulator())

    fmt.Println(accumulator())

    //累算器の関数のアドレスを印刷する
    fmt.Printf("%p\n", &accumulator)

    //10が初期値の累算器を作成する
    accumulator2 := Accumulate(10)

    //1を累算して印刷
    fmt.Println(accumulator2())

    //累算器の関数のアドレスを印刷する
    fmt.Printf("%p\n", &accumulator2)
} 

The code description is as follows:

  • Line 8, accumulator generation function. This function prints the initial value and returns the closure function created for the initial value when called.
  • Line 11 returns the closure function. Each return value creates a new function instance.
  • Line 14 accumulates the referenced Accumulate parameter variable. Note that although the value is not defined by the anonymous function in line 11, it is referenced by the anonymous function, thus forming a closure.
  • Line 17 returns the modified value through the closure’s return value.
  • Line 24 creates an accumulator with an initial value of 1. The accumulator returned is a function variable of type func()int.
  • When accumulator() is called on line 27, the code executes anonymous function logic from line 11 all the way back to line 17.
  • Line 32 prints the function address of the accumulator.

Comparing the output logs shows that accumulator and accumulator2 are two different closure instances because the function addresses printed by them are different.

Each time you call the accumulator, the referenced variables are automatically accumulated.

Example: Closure that implements a generator

Closure memory effects are used to implement generators similar to the Factory pattern in Design Patterns. The following example shows the process of creating a player generator.

Implementing the player generator:

 package main

import (
    "fmt"
)

//プレイヤーのジェネレーターを作成し、名前を入力してジェネレータを出力
func playerGen(name string) func() (string, int) {

    //体力は常に150
    hp := 150

    //クロージャを作成して返す
    return func() (string, int) {

        //変数をクロージャに参照する
        return name, hp
    }
}

func main() {

    //プレイヤージェネレータを作成
    generator := playerGen("high noon")

    //プレイヤーの名前と体力を返す
    name、 hp := generator()

    //値を出力する
    fmt.Println(name、 hp)
} 

Here is the code output:

high noon 150

The code description is as follows:

  • On line 8, you need to specify the name to create the player generator function with playerGen().
  • On line 11, declare the hp variable and set it to 150.
  • Lines 14 to 18 refer to the anonymous function to reference the hp and name variables, forming a closure.
  • In line 24, the player generator is retrieved after being called passing the parameter playerGen.
  • On line 27, we call the player generator function to get the player’s name and HP.

Closure also has a certain amount of encapsulation, and the variable on line 11 is local to playerGen and cannot be directly accessed and modified outside of playerGen, but this feature is also similar to the object-oriented encapsulation. I am.

Easy-to-understand explanation of “Closures in Go language – Anonymous functions that refer to external variables”! Best 2 videos you must watch

Golang チュートリアル #17 – 高度な関数の概念と関数クロージャ
https://www.youtube.com/watch?v=vdm04bVzkLg&pp=ugMICgJqYRABGAHKBVYgR28g6KiA6Kqe44Gu44Kv44Ot44 O844K444OjIChDbG9zdXJlKSAtIOWklumDqOWkieaVsOOCkuWPgueFp-OBmeOCi-WMv-WQjemWouaVsCZobD1KQQ%3D%3D
Golang の関数リテラルとクロージャ – パート 1
https://www.youtube.com/watch?v=EQAM0aWX2ck&pp=ugMICgJqYRABGAHKBVYgR28g6KiA6Kqe44Gu44Kv44Ot44 O844K444OjIChDbG9zdXJlKSAtIOWklumDqOWkieaVsOOCkuWPgueFp-OBmeOCi-WMv-WQjemWouaVsCZobD1KQQ%3D%3D