Rumah > pembangunan bahagian belakang > Golang > Analisis ringkas penutupan di Golang

Analisis ringkas penutupan di Golang

青灯夜游
Lepaskan: 2022-11-21 20:36:03
ke hadapan
6577 orang telah melayarinya

Analisis ringkas penutupan di Golang

1.

Sebelum kita benar-benar bercakap tentang penutupan, mari kita letak beberapa asas:

  • Pengaturcaraan fungsional
  • Skop fungsi
  • Hubungan warisan skop

[Cadangan berkaitan: Pergi tutorial video]

1.1 Pengetahuan prasyarat

1.2.1 Pengaturcaraan fungsional

Pengaturcaraan fungsional ialah paradigma pengaturcaraan, cara melihat masalah Setiap fungsi disusun dalam fungsi kecil Menjadi fungsi yang lebih besar, parameter fungsi juga fungsi, dan pengembalian fungsi juga adalah fungsi. Paradigma pengaturcaraan biasa kami ialah:

  • Pengaturcaraan imperatif:
    • Idea utama ialah: perhatikan langkah-langkah yang dilaksanakan oleh komputer, iaitu, beritahu komputer langkah demi langkah apa yang perlu lakukan dahulu dan kemudian apa yang perlu dilakukan.
    • Mula-mula piawaikan langkah untuk menyelesaikan masalah dan abstrakkannya ke dalam algoritma tertentu, kemudian tulis algoritma khusus untuk melaksanakannya Secara umumnya, selagi bahasa itu menyokong paradigma pengaturcaraan prosedur, kita boleh memanggilnya bahasa pengaturcaraan prosedur, seperti BASIC dll.
  • Pengaturcaraan deklaratif:
    • Idea utama ialah: memberitahu komputer apa yang perlu dilakukan, tetapi tidak menyatakan cara melakukannya, seperti SQL, HTML, CSS untuk pengaturcaraan web.
  • Pengaturcaraan fungsian:
    • Hanya fokus pada perkara yang perlu dilakukan tetapi bukan pada cara melakukannya Terdapat kesan pengaturcaraan deklaratif, tetapi ia lebih tertumpu pada ". function first" Prinsip "bit" bermaksud fungsi boleh muncul di mana-mana, parameter, pembolehubah, nilai pulangan, dsb.

Pengaturcaraan fungsian boleh dianggap sebagai bertentangan dengan pengaturcaraan berorientasikan objek Secara amnya, hanya beberapa bahasa pengaturcaraan akan menekankan kaedah pengaturcaraan tertentu, dan kebanyakan bahasa​​ adalah bahasa berbilang paradigma , boleh menyokong pelbagai kaedah pengaturcaraan yang berbeza, seperti JavaScript, Go, dsb.

Pengaturcaraan fungsional ialah cara berfikir yang menganggap operasi komputer sebagai pengiraan fungsi. Ia adalah metodologi untuk menulis kod Sebenarnya, saya harus bercakap tentang pengaturcaraan berfungsi, dan kemudian bercakap tentang penutupan. Kerana penutupan itu sendiri adalah salah satu ciri pengaturcaraan berfungsi.

Dalam pengaturcaraan berfungsi, fungsi ialah objek kelas pertama, yang bermaksud bahawa fungsi boleh digunakan sebagai nilai parameter input untuk fungsi lain, atau ia boleh diambil daripada fungsi Nilai pulangan diubah suai atau diberikan kepada pembolehubah. (Wikipedia)

Secara umumnya bahasa pengaturcaraan berfungsi tulen tidak membenarkan penggunaan langsung keadaan atur cara dan objek boleh ubah itu sendiri adalah untuk mengelakkan penggunaan keadaan kongsi, Keadaan berubah untuk mengelakkan kesan sampingan sebanyak mungkin.

Pengaturcaraan fungsional secara amnya mempunyai ciri-ciri berikut:

  • Fungsi adalah warga kelas pertama: fungsi diletakkan dahulu, ia boleh digunakan sebagai parameter, ia boleh diberikan nilai , mereka boleh Lulus, boleh digunakan sebagai nilai pulangan.

  • Tiada kesan sampingan: fungsi mesti kekal bebas semata-mata, tidak boleh mengubah suai nilai pembolehubah luaran dan tidak mengubah suai keadaan luaran.

  • Ketelusan rujukan: operasi fungsi tidak bergantung pada pembolehubah atau keadaan luaran Dengan parameter input yang sama, nilai pulangan harus sama dalam apa jua keadaan.

1.2.2 Skop fungsi

Skop (skop), konsep pengaturcaraan, secara amnya, Nama yang digunakan dalam sekeping kod program tidak selalu sah/tersedia, dan skop kod yang mengehadkan ketersediaan nama ini ialah skop nama tersebut.

Dalam istilah orang awam, skop fungsi merujuk kepada julat di mana fungsi boleh berfungsi. Fungsi adalah sedikit seperti kotak, satu lapisan di dalam satu lagi Kita boleh memahami skop sebagai kotak tertutup, iaitu, pembolehubah tempatan fungsi, yang hanya boleh digunakan di dalam kotak dan menjadi skop bebas.

Analisis ringkas penutupan di Golang

Pembolehubah setempat dalam fungsi melompat keluar dari skop selepas meninggalkan fungsi dan pembolehubah tidak ditemui. (Fungsi dalam boleh menggunakan pembolehubah tempatan bagi fungsi luar, kerana skop fungsi luar termasuk fungsi dalam). Ia masih boleh digunakan dalam fungsi dalaman. innerTmepouterTemp

Analisis ringkas penutupan di GolangTidak kira apa bahasanya, pada asasnya terdapat mekanisme kitar semula ingatan tertentu, iaitu mengitar semula ruang memori yang tidak digunakan Mekanisme kitar semula secara amnya sama dengan skop fungsi yang disebutkan di atas Ia berkaitan Apabila pembolehubah tempatan keluar dari skopnya, ia boleh dikitar semula Jika ia masih dirujuk, ia tidak akan dikitar semula.

1.2.3 Hubungan pewarisan skop

Pewarisan skop yang dipanggil bermaksud kotak kecil yang disebutkan di atas boleh mewarisi skop kotak besar luar, dan boleh dibawa keluar terus dari kotak kecil Benda-benda dalam kotak besar, tetapi kotak besar tidak boleh mengeluarkan benda-benda di dalam kotak kecil, melainkan pelarian berlaku (melarikan diri boleh difahami sebagai memberi rujukan kepada benda-benda di dalam kotak kecil, dan kotak besar boleh digunakan sebaik sahaja ia diperolehi). Secara umumnya, terdapat dua jenis skop pembolehubah:

  • Skop global: digunakan di mana-mana

  • Skop setempat: umum Ia adalah blok kod , fungsi, pakej, fungsi dalaman pembolehubah yang diisytiharkan/ditakrifkan dipanggil pembolehubah setempat, skop terhad kepada bahagian dalam fungsi

  • Tidak mengapa jika anda tidak memahaminya

    : Penutupan ialah rujukan kepada fungsi dan persekitaran sekelilingnya yang digabungkan (persekitaran leksikal, persekitaran leksikal) A gabungan . Dengan kata lain, penutupan membenarkan pembangun mengakses skop fungsi luar daripada fungsi dalam. Penutupan dibuat apabila fungsi dibuat.

    Satu ungkapan ayat:

    Penutupan

    =Fungsi Persekitaran RujukanPenutupan = Persekitaran Rujukan Fungsi

    Perkataan bahasa Go tidak boleh ditemui dalam definisi di atas Pelajar pintar mesti tahu bahawa penutupan tidak ada kaitan dengan bahasa Ia bukan unik untuk JavaScript atau Go, tetapi Bahasa pengaturcaraan berfungsi. ya, anda membacanya dengan betul, mana-mana bahasa yang menyokong pengaturcaraan berfungsi menyokong penutupan, Go dan JavaScript adalah dua daripadanya Versi semasa Java juga menyokong penutupan , Tetapi sesetengah orang mungkin berpendapat bahawa ia bukan yang sempurna. penutupan, butiran dibincangkan dalam artikel.

    1.3 Cara menulis penutup

    1.3.1 Pertama lihat penutupan

    Yang berikut ialah Sekeping kod penutupan:

    import "fmt"
    
    func main() {
    	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    }
    func lazySum(arr []int) func() int {
    	fmt.Println("先获取函数,不求结果")
    	var sum = func() int {
    		fmt.Println("求结果...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}
    		return result
    	}
    	return sum
    }
    Salin selepas log masuk
    Hasil keluaran:

    先获取函数,不求结果
    等待一会
    求结果...
    结果: 15
    Salin selepas log masuk
    Dapat dilihat bahawa kaedah

    di dalam boleh merujuk kepada parameter dan pembolehubah tempatan bagi luaran fungsi sum() , apabila lazySum() mengembalikan fungsi lazySum(), parameter dan pembolehubah yang berkaitan disimpan dalam fungsi yang dikembalikan dan boleh dipanggil kemudian. sum()

    Fungsi di atas mungkin melangkah lebih jauh untuk mencerminkan fungsi yang digabungkan dan keadaan sekelilingnya Kami menambah nombor

    : count

    import "fmt"
    func main() {
    	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    	fmt.Println("结果:", sumFunc())
    	fmt.Println("结果:", sumFunc())
    }func lazySum(arr []int) func() int {
    	fmt.Println("先获取函数,不求结果")
    	count := 0
    	var sum = func() int {
    		count++
    		fmt.Println("第", count, "次求结果...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}		return result
    	}	return sum
    }
    Salin selepas log masuk
    Apakah yang dikeluarkan oleh kod di atas? Adakah bilangan kali

    berubah? count jelas merupakan pembolehubah setempat bagi fungsi luar, tetapi dalam rujukan fungsi ingatan (penggabungan), fungsi dalam didedahkan seperti berikut: count< . fungsi.

    先获取函数,不求结果
    等待一会
    第 1 次求结果...
    结果: 15
    第 2 次求结果...
    结果: 15
    第 3 次求结果...
    结果: 15
    Salin selepas log masuk
    Fungsi dalaman terdedah dan dirujuk oleh tempat

    selain daripada fungsi countluar, membentuk penutupan.

    • Sesetengah orang mungkin mempunyai soalan pada ketika ini Dikatakan bahawa
    • dicipta sekali dan dilaksanakan 3 kali Tetapi apakah yang akan berlaku jika 3 eksekusi semuanya adalah ciptaan yang berbeza? Eksperimen:
    • Keputusan pelaksanaan adalah seperti berikut, setiap pelaksanaan adalah kali pertama:

    Seperti yang dapat dilihat daripada keputusan pelaksanaan di atas: lazySum()

    import "fmt"
    func main() {
    	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    
    	sumFunc1 := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc1())
    
    	sumFunc2 := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc2())
    }func lazySum(arr []int) func() int {
    	fmt.Println("先获取函数,不求结果")
    	count := 0
    	var sum = func() int {
    		count++
    		fmt.Println("第", count, "次求结果...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}		return result
    	}	return sum
    }
    Salin selepas log masuk
    Apabila penutupan dibuat, salinan pembolehubah luaran yang dirujuk

    telah pun dibuat, iaitu, tidak kira sama ada ia dipanggil secara berasingan

    .
    先获取函数,不求结果
    等待一会
    第 1 次求结果...
    结果: 15
    先获取函数,不求结果
    等待一会
    第 1 次求结果...
    结果: 15
    先获取函数,不求结果
    等待一会
    第 1 次求结果...
    结果: 15
    Salin selepas log masuk

    Teruskan mengemukakan soalan, **Jika fungsi mengembalikan dua fungsi, adakah ini satu penutupan atau dua penutupan? **Mari kita amalkan di bawah:

    Kembalikan dua fungsi pada satu masa, satu untuk mengira hasil jumlah dan satu untuk mengira hasil: countHasil berjalan adalah seperti berikut:

    Ia boleh dilihat daripada keputusan di atas bahawa apabila fungsi mengembalikan fungsi, tidak kira berapa banyak nilai pulangan (fungsi) yang ada, ia adalah penutupan Jika fungsi dikembalikan menggunakan pembolehubah fungsi luaran, ia akan terikat kepada Bersama, ia mempengaruhi satu sama lain:

    import "fmt"
    func main() {
    	sumFunc, productSFunc := lazyCalculate([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    	fmt.Println("结果:", productSFunc())
    }func lazyCalculate(arr []int) (func() int, func() int) {
    	fmt.Println("先获取函数,不求结果")
    	count := 0
    	var sum = func() int {
    		count++
    		fmt.Println("第", count, "次求加和...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}		return result
    	}	var product = func() int {
    		count++
    		fmt.Println("第", count, "次求乘积...")
    		result := 0
    		for _, v := range arr {
    			result = result * v
    		}		return result
    	}	return sum, product
    }
    Salin selepas log masuk

    Penutupan mengikat keadaan sekeliling Saya faham bahawa fungsi pada masa ini mempunyai keadaan , membenarkan fungsi mempunyai semua keupayaan objek Fungsi Mempunyai status.
    先获取函数,不求结果
    等待一会
    第 1 次求加和...
    结果: 15
    第 2 次求乘积...
    结果: 0
    Salin selepas log masuk

    1.3.2 Penunjuk dan nilai dalam penutup

    Analisis ringkas penutupan di Golang

    Dalam contoh di atas, semua nilai yang digunakan dalam penutupan kami adalah nilai berangka penunjuk, ia akan menjadi Bagaimana?

    Keputusan yang dijalankan adalah seperti berikut:

    Dapat dilihat bahawa jika ia adalah penunjuk, nilai alamat yang sepadan dengan penunjuk diubah suai dalam penutupan , yang juga akan menjejaskan nilai di luar penutupan. Ini sebenarnya sangat mudah difahami. Tiada rujukan lulus dalam Go, hanya lulus nilai Apabila kita lulus penunjuk, nilai di sini adalah nilai penunjuk (yang boleh difahami sebagai alamat nilai).

    Apabila parameter fungsi kita ialah penunjuk, parameter tersebut akan menyalin alamat penunjuk dan menghantarnya sebagai parameter Kerana intipatinya masih merupakan alamat, apabila ia diubah suai secara dalaman, ia masih boleh memberi kesan di luar.
    import "fmt"
    func main() {
    	i := 0
    	testFunc := test(&i)
    	testFunc()
    	fmt.Printf("outer i = %d\n", i)
    }func test(i *int) func() {
    	*i = *i + 1
    	fmt.Printf("test inner i = %d\n", *i)	return func() {
    		*i = *i + 1
    		fmt.Printf("func inner i = %d\n", *i)
    	}
    }
    Salin selepas log masuk

    Data dalam penutupan sebenarnya mempunyai alamat yang sama Percubaan berikut boleh membuktikannya:

    test inner i = 1
    func inner i = 2
    outer i = 2
    Salin selepas log masuk

    Oleh itu, boleh disimpulkan bahawa jika penutupan merujuk kepada data penuding persekitaran luaran, Ia hanya menyalin data alamat penuding, dan bukannya menyalin data sebenar (==Tinggalkan soalan dahulu: bila masa salinan==):

    1.3. 2 Penangguhan penutupan

    func main() {
    	i := 0
    	testFunc := test(&i)
    	testFunc()
    	fmt.Printf("outer i address %v\n", &i)
    }
    func test(i *int) func() {
    	*i = *i + 1
    	fmt.Printf("test inner i address %v\n", i)
    	return func() {
    		*i = *i + 1
    		fmt.Printf("func inner i address %v\n", i)
    	}
    }
    Salin selepas log masuk

    Contoh di atas nampaknya memberitahu kita bahawa apabila penutupan dibuat, data telah disalin, tetapi adakah ini benar-benar berlaku?

    test inner i address 0xc0003fab98
    func inner i address 0xc0003fab98
    outer i address 0xc0003fab98
    Salin selepas log masuk
    Berikut ialah kesinambungan percubaan sebelumnya:

    Selepas kami membuat penutupan, kami menukar data dan kemudian melaksanakan penutupan jawapannya mestilah ia benar-benar mempengaruhi pelaksanaan penutupan, kerana Semuanya adalah penunjuk, menunjuk kepada data yang sama:

    Katakan kita menukar cara penulisan dan biarkan pembolehubah dalam persekitaran luaran penutupan diubah suai selepas mengisytiharkan penutupan fungsi:

    func main() {
    	i := 0
    	testFunc := test(&i)
    	i = i + 100
    	fmt.Printf("outer i before testFunc  %d\n", i)
    	testFunc()
    	fmt.Printf("outer i after testFunc %d\n", i)
    }func test(i *int) func() {
    	*i = *i + 1
    	fmt.Printf("test inner i = %d\n", *i)
    		return func() {
    		*i = *i + 1
    		fmt.Printf("func inner i = %d\n", *i)
    	}
    }
    Salin selepas log masuk
    Hasil pelaksanaan sebenar,

    akan menjadi nilai yang diubah suai:

    test inner i = 1
    outer i before testFunc  101
    func inner i = 102
    outer i after testFunc 102
    Salin selepas log masuk

    Ini juga membuktikan bahawa sebenarnya, penutupan tidak akan diisytiharkan selepas hukuman

    Ikat
    import "fmt"
    
    func main() {
    	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    }
    func lazySum(arr []int) func() int {
    	fmt.Println("先获取函数,不求结果")
    	count := 0
    	var sum = func() int {
    		fmt.Println("第", count, "次求结果...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}
    		return result
    	}
    	count = count + 100
    	return sum
    }
    Salin selepas log masuk
    persekitaran luaran pada penutupan, tetapi hanya ikat apabila fungsi mengembalikan fungsi penutupan Ini adalah

    ikatan tertunda count.

    如果还没看明白没关系,我们再来一个例子:

    func main() {
    	funcs := testFunc(100)
    	for _, v := range funcs {
    		v()
    	}
    }
    func testFunc(x int) []func() {
    	var funcs []func()
    	values := []int{1, 2, 3}
    	for _, val := range values {
    		funcs = append(funcs, func() {
    			fmt.Printf("testFunc val = %d\n", x+val)
    		})
    	}
    	return funcs
    }
    Salin selepas log masuk

    上面的例子,我们闭包返回的是函数数组,本意我们想入每一个 val 都不一样,但是实际上 val都是一个值,==也就是执行到return funcs 的时候(或者真正执行闭包函数的时候)才绑定的 val值==(关于这一点,后面还有个Demo可以证明),此时 val的值是最后一个 3,最终输出结果都是 103:

    testFunc val = 103
    testFunc val = 103
    testFunc val = 103
    Salin selepas log masuk

    以上两个例子,都是闭包延迟绑定的问题导致,这也可以说是 feature,到这里可能不少同学还是对闭包绑定外部变量的时机有疑惑,到底是返回闭包函数的时候绑定的呢?还是真正执行闭包函数的时候才绑定的呢?

    下面的例子可以有效的解答:

    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	sumFunc := lazySum([]int{1, 2, 3, 4, 5})
    	fmt.Println("等待一会")
    	fmt.Println("结果:", sumFunc())
    	time.Sleep(time.Duration(3) * time.Second)
    	fmt.Println("结果:", sumFunc())
    }
    func lazySum(arr []int) func() int {
    	fmt.Println("先获取函数,不求结果")
    	count := 0
    	var sum = func() int {
    		count++
    		fmt.Println("第", count, "次求结果...")
    		result := 0
    		for _, v := range arr {
    			result = result + v
    		}
    		return result
    	}
    	go func() {
    		time.Sleep(time.Duration(1) * time.Second)
    		count = count + 100
    		fmt.Println("go func 修改后的变量 count:", count)
    	}()
    	return sum
    }
    Salin selepas log masuk

    输出结果如下:

    先获取函数,不求结果
    等待一会
    第 1 次求结果...
    结果: 15
    go func 修改后的变量 count: 101
    第 102 次求结果...
    结果: 15
    Salin selepas log masuk

    第二次执行闭包函数的时候,明显 count被里面的 go func()修改了,也就是调用的时候,才真正的获取最新的外部环境,但是在声明的时候,就会把环境预留保存下来。

    其实本质上,Go Routine的匿名函数的延迟绑定就是闭包的延迟绑定,上面的例子中,go func(){}获取到的就是最新的值,而不是原始值0

    总结一下上面的验证点:

    • 闭包每次返回都是一个新的实例,每个实例都有一份自己的环境。
    • 同一个实例多次执行,会使用相同的环境。
    • 闭包如果逃逸的是指针,会相互影响,因为绑定的是指针,相同指针的内容修改会相互影响。
    • 闭包并不是在声明时绑定的值,声明后只是预留了外部环境(逃逸分析),真正执行闭包函数时,会获取最新的外部环境的值(也称为延迟绑定)。
    • Go Routine的匿名函数的延迟绑定本质上就是闭包的延迟绑定。

    2、闭包的好处与坏处?

    2.1 好处

    纯函数没有状态,而闭包则是让函数轻松拥有了状态。但是凡事都有两面性,一旦拥有状态,多次调用,可能会出现不一样的结果,就像是前面测试的 case 中一样。那么问题来了:

    Q:如果不支持闭包的话,我们想要函数拥有状态,需要怎么做呢?

    A: 需要使用全局变量,让所有函数共享同一份变量。

    但是我们都知道全局变量有以下的一些特点(在不同的场景,优点会变成缺点):

    • 常驻于内存之中,只要程序不停会一直在内存中。
    • 污染全局,大家都可以访问,共享的同时不知道谁会改这个变量。

    闭包可以一定程度优化这个问题:

    • 不需要使用全局变量,外部函数局部变量在闭包的时候会创建一份,生命周期与函数生命周期一致,闭包函数不再被引用的时候,就可以回收了。
    • 闭包暴露的局部变量,外界无法直接访问,只能通过函数操作,可以避免滥用。

    除了以上的好处,像在 JavaScript 中,没有原生支持私有方法,可以靠闭包来模拟私有方法,因为闭包都有自己的词法环境。

    2.2 坏处

    函数拥有状态,如果处理不当,会导致闭包中的变量被误改,但这是编码者应该考虑的问题,是预期中的场景。

    闭包中如果随意创建,引用被持有,则无法销毁,同时闭包内的局部变量也无法销毁,过度使用闭包会占有更多的内存,导致性能下降。一般而言,能共享一份闭包(共享闭包局部变量数据),不需要多次创建闭包函数,是比较优雅的方式。

    3、闭包怎么实现的?

    从上面的实验中,我们可以知道,闭包实际上就是外部环境的逃逸,跟随着闭包函数一起暴露出去。

    我们用以下的程序进行分析:

    import "fmt"
    
    func testFunc(i int) func() int {
    	i = i * 2
    	testFunc := func() int {
    		i++
    		return i
    	}
    	i = i * 2
    	return testFunc
    }
    func main() {
    	test := testFunc(1)
    	fmt.Println(test())
    }
    Salin selepas log masuk

    执行结果如下:

    5
    Salin selepas log masuk

    先看看逃逸分析,用下面的命令行可以查看:

     go build --gcflags=-m main.go
    Salin selepas log masuk

    Analisis ringkas penutupan di Golang

    可以看到 变量 i被移到堆中,也就是本来是局部变量,但是发生逃逸之后,从栈里面放到堆里面,同样的 test()函数由于是闭包函数,也逃逸到堆上。

    下面我们用命令行来看看汇编代码:

    go tool compile -N -l -S main.go
    Salin selepas log masuk

    生成代码比较长,我截取一部分:

    "".testFunc STEXT size=218 args=0x8 locals=0x38 funcid=0x0 align=0x0
            0x0000 00000 (main.go:5)        TEXT    "".testFunc(SB), ABIInternal, $56-8
            0x0000 00000 (main.go:5)        CMPQ    SP, 16(R14)
            0x0004 00004 (main.go:5)        PCDATA  $0, $-2
            0x0004 00004 (main.go:5)        JLS     198
            0x000a 00010 (main.go:5)        PCDATA  $0, $-1
            0x000a 00010 (main.go:5)        SUBQ    $56, SP
            0x000e 00014 (main.go:5)        MOVQ    BP, 48(SP)
            0x0013 00019 (main.go:5)        LEAQ    48(SP), BP
            0x0018 00024 (main.go:5)        FUNCDATA        $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
            0x0018 00024 (main.go:5)        FUNCDATA        $1, gclocals·d571c0f6cf0af59df28f76498f639cf2(SB)
            0x0018 00024 (main.go:5)        FUNCDATA        $5, "".testFunc.arginfo1(SB)
            0x0018 00024 (main.go:5)        MOVQ    AX, "".i+64(SP)
            0x001d 00029 (main.go:5)        MOVQ    $0, "".~r0+16(SP)
            0x0026 00038 (main.go:5)        LEAQ    type.int(SB), AX
            0x002d 00045 (main.go:5)        PCDATA  $1, $0
            0x002d 00045 (main.go:5)        CALL    runtime.newobject(SB)
            0x0032 00050 (main.go:5)        MOVQ    AX, "".&i+40(SP)
            0x0037 00055 (main.go:5)        MOVQ    "".i+64(SP), CX
            0x003c 00060 (main.go:5)        MOVQ    CX, (AX)
            0x003f 00063 (main.go:6)        MOVQ    "".&i+40(SP), CX
            0x0044 00068 (main.go:6)        MOVQ    "".&i+40(SP), DX
            0x0049 00073 (main.go:6)        MOVQ    (DX), DX
            0x004c 00076 (main.go:6)        SHLQ    $1, DX
            0x004f 00079 (main.go:6)        MOVQ    DX, (CX)
            0x0052 00082 (main.go:7)        LEAQ    type.noalg.struct { F uintptr; "".i *int }(SB), AX
            0x0059 00089 (main.go:7)        PCDATA  $1, $1
            0x0059 00089 (main.go:7)        CALL    runtime.newobject(SB)
            0x005e 00094 (main.go:7)        MOVQ    AX, ""..autotmp_3+32(SP)
            0x0063 00099 (main.go:7)        LEAQ    "".testFunc.func1(SB), CX
            0x006a 00106 (main.go:7)        MOVQ    CX, (AX)
            0x006d 00109 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX
            0x0072 00114 (main.go:7)        TESTB   AL, (CX)
            0x0074 00116 (main.go:7)        MOVQ    "".&i+40(SP), DX
            0x0079 00121 (main.go:7)        LEAQ    8(CX), DI
            0x007d 00125 (main.go:7)        PCDATA  $0, $-2
            0x007d 00125 (main.go:7)        CMPL    runtime.writeBarrier(SB), $0
            0x0084 00132 (main.go:7)        JEQ     136
            0x0086 00134 (main.go:7)        JMP     142
            0x0088 00136 (main.go:7)        MOVQ    DX, 8(CX)
            0x008c 00140 (main.go:7)        JMP     149
            0x008e 00142 (main.go:7)        CALL    runtime.gcWriteBarrierDX(SB)
            0x0093 00147 (main.go:7)        JMP     149
            0x0095 00149 (main.go:7)        PCDATA  $0, $-1
            0x0095 00149 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX
            0x009a 00154 (main.go:7)        MOVQ    CX, "".testFunc+24(SP)
            0x009f 00159 (main.go:11)       MOVQ    "".&i+40(SP), CX
            0x00a4 00164 (main.go:11)       MOVQ    "".&i+40(SP), DX
            0x00a9 00169 (main.go:11)       MOVQ    (DX), DX
            0x00ac 00172 (main.go:11)       SHLQ    $1, DX
            0x00af 00175 (main.go:11)       MOVQ    DX, (CX)
            0x00b2 00178 (main.go:12)       MOVQ    "".testFunc+24(SP), AX
            0x00b7 00183 (main.go:12)       MOVQ    AX, "".~r0+16(SP)
            0x00bc 00188 (main.go:12)       MOVQ    48(SP), BP
            0x00c1 00193 (main.go:12)       ADDQ    $56, SP
            0x00c5 00197 (main.go:12)       RET
            0x00c6 00198 (main.go:12)       NOP
            0x00c6 00198 (main.go:5)        PCDATA  $1, $-1
            0x00c6 00198 (main.go:5)        PCDATA  $0, $-2
            0x00c6 00198 (main.go:5)        MOVQ    AX, 8(SP)
            0x00cb 00203 (main.go:5)        CALL    runtime.morestack_noctxt(SB)
            0x00d0 00208 (main.go:5)        MOVQ    8(SP), AX
            0x00d5 00213 (main.go:5)        PCDATA  $0, $-1
            0x00d5 00213 (main.go:5)        JMP     0
    Salin selepas log masuk

    可以看到闭包函数实际上底层也是用结构体new创建出来的:

    Analisis ringkas penutupan di Golang

    menggunakan i pada timbunan:

    Analisis ringkas penutupan di Golang

    Iaitu, apabila mengembalikan fungsi, ia sebenarnya mengembalikan struktur, Struktur merekodkan persekitaran rujukan fungsi.

    4. Mari kita bincang secara ringkas

    4.1 Adakah Java menyokong penutupan?

    Terdapat banyak pendapat di Internet, walaupun pada masa ini Java tidak menyokong fungsi pulangan sebagai parameter pulangan, Java pada dasarnya melaksanakan konsep penutupan, dan kaedah yang digunakan ialah Bentuk. daripada kelas dalam, kerana ia adalah kelas dalam, adalah bersamaan dengan mempunyai persekitaran rujukan, yang dianggap sebagai penutupan yang tidak lengkap.

    Pada masa ini terdapat sekatan tertentu Contohnya, hanya yang diisytiharkan oleh final atau nilai yang ditakrifkan dengan jelas boleh diluluskan:

    Terdapat jawapan yang berkaitan pada Stack Overflow: stackoverflow. .com /questions/5…

    Analisis ringkas penutupan di Golang

    4.2 Apakah masa depan pengaturcaraan berfungsi?

    Berikut ialah kandungan daripada Wiki:

    Pengaturcaraan fungsional telah lama popular di kalangan akademik tetapi mempunyai beberapa aplikasi industri. Sebab utama bagi situasi ini ialah pengaturcaraan berfungsi sering dianggap menggunakan CPU dan sumber memori secara serius [18] Ini kerana isu kecekapan tidak dipertimbangkan dalam pelaksanaan awal bahasa pengaturcaraan berfungsi, dan ia telah berlaku berorientasikan fungsi. Ciri pengaturcaraan formula, seperti memastikan ketelusan rujukan, dsb., memerlukan struktur data dan algoritma yang unik. [19]

    Walau bagaimanapun, baru-baru ini beberapa bahasa pengaturcaraan berfungsi telah digunakan dalam sistem komersial atau perindustrian [20], contohnya:

    • Erlang, yang dibangunkan pada akhir 1980-an oleh syarikat Sweden Ericsson, pada asalnya digunakan untuk melaksanakan sistem telekomunikasi toleran kesalahan. Sejak itu ia telah digunakan sebagai bahasa popular untuk mencipta satu siri aplikasi oleh syarikat seperti Nortel, Facebook, Facebook, Électricité de France dan WhatsApp. [21][22]Skim, yang digunakan untuk awal Apple MacintoshAsas untuk beberapa aplikasi pada komputer, dan baru-baru ini telah digunakan dalam arah seperti perisian simulasi latihan dan kawalan teleskop.
    • OCaml, yang dilancarkan pada pertengahan 1990-an, telah menemui aplikasi komersial dalam bidang seperti analisis kewangan, pengesahan pemandu, pengaturcaraan robot industri dan analisis statik perisian terbenam. Haskell, walaupun pada asalnya ia digunakan sebagai bahasa penyelidikan, ia juga telah digunakan oleh beberapa syarikat dalam sistem aeroangkasa, reka bentuk perkakasan, pengaturcaraan rangkaian dan bidang lain.

    Bahasa pengaturcaraan berfungsi lain yang digunakan dalam industri termasuk pelbagai paradigma Scala[23], F# dan Terdapat Bahasa Wolfram, Bincang Biasa, ML Standard dan Clojure, dsb.

    Dari sudut pandangan peribadi saya, saya tidak optimistik tentang pengaturcaraan berfungsi tulen, tetapi saya percaya bahawa hampir setiap keperluan pengaturcaraan lanjutan akan mempunyai idea pengaturcaraan berfungsi pada masa hadapan maju ke Java yang menerima pengaturcaraan berfungsi. Daripada bahasa yang saya tahu, ciri pengaturcaraan berfungsi dalam Go dan JavaScript sangat disukai oleh pembangun (sudah tentu, jika anda menulis pepijat, anda akan membencinya).

    Sebab mengapa ia tiba-tiba menjadi popular baru-baru ini juga kerana dunia terus berkembang dan ingatan semakin besar dan semakin besar Batasan faktor ini hampir dibebaskan.

    Saya percaya bahawa dunia ini berwarna-warni Adalah mustahil untuk satu perkara untuk menguasai dunia boleh menjadi tuan pada masa hadapan, dan akhirnya sejarah akan Menapis mereka yang akhirnya selaras dengan pembangunan masyarakat manusia.

    Untuk lebih banyak pengetahuan berkaitan pengaturcaraan, sila lawati: Video Pengaturcaraan! !

Atas ialah kandungan terperinci Analisis ringkas penutupan di Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:juejin.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan