匿名函數:顧名思義就是沒有名字的函數。很多語言都有如:java,js,php等,其中js最鍾情。匿名函數最大的用途是來模擬塊級作用域,避免數據污染的。
今天主要講一下Golang語言的匿名函數和閉包。
匿名函數
示例:
1、
package main
import (
"fmt"
)
func main() {
f:=func(){
fmt.Println("hello world")
}
f()//hello world
fmt.Printf("%T\n", f) //打印 func()
}
2、帶參數
package main
import (
"fmt"
)
func main() {
f:=func(args string){
fmt.Println(args)
}
f("hello world")//hello world
//或
(func(args string){
fmt.Println(args)
})("hello world")//hello world
//或
func(args string) {
fmt.Println(args)
}("hello world") //hello world
}
3、帶返回值
package main
import "fmt"
func main() {
f:=func()string{
return "hello world"
}
a:=f()
fmt.Println(a)//hello world
}
4、多個匿名函數
package main
import "fmt"
func main() {
f1,f2:=F(1,2)
fmt.Println(f1(4))//6
fmt.Println(f2())//6
}
func F(x, y int)(func(int)int,func()int) {
f1 := func(z int) int {
return (x + y) * z / 2
}
f2 := func() int {
return 2 * (x + y)
}
return f1,f2
}
閉包(closure)
閉包:説白了就是函數的嵌套,內層的函數可以使用外層函數的所有變量,即使外層函數已經執行完畢。
示例:
1、
package main
import "fmt"
func main() {
a := Fun()
b:=a("hello ")
c:=a("hello ")
fmt.Println(b)//worldhello
fmt.Println(c)//worldhello hello
}
func Fun() func(string) string {
a := "world"
return func(args string) string {
a += args
return a
}
}
2、
package main
import "fmt"
func main() {
a := Fun()
d := Fun()
b:=a("hello ")
c:=a("hello ")
e:=d("hello ")
f:=d("hello ")
fmt.Println(b)//worldhello
fmt.Println(c)//worldhello hello
fmt.Println(e)//worldhello
fmt.Println(f)//worldhello hello
}
func Fun() func(string) string {
a := "world"
return func(args string) string {
a += args
return a
}
}
注意兩次調用F(),維護的不是同一個a變量。
3、
package main
import "fmt"
func main() {
a := F()
a[0]()//0xc00004c080 3
a[1]()//0xc00004c080 3
a[2]()//0xc00004c080 3
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
b[i] = func() {
fmt.Println(&i,i)
}
}
return b
}
閉包通過引用的方式使用外部函數的變量。例中只調用了一次函數F,構成一個閉包,i 在外部函數B中定義,所以閉包維護該變量 i ,a[0]、a[1]、a[2]中的 i 都是閉包中 i 的引用。因此執行,i 的值已經變為3,故再調用a[0]()時的輸出是3而不是0。
4、如何避免上面的BUG ,用下面的方法,注意和上面示例對比。
package main
import "fmt"
func main() {
a := F()
a[0]() //0xc00000a0a8 0
a[1]() //0xc00000a0c0 1
a[2]() //0xc00000a0c8 2
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
b[i] = (func(j int) func() {
return func() {
fmt.Println(&j, j)
}
})(i)
}
return b
}
或者
package main
import "fmt"
func main() {
a := F()
a[0]() //0xc00004c080 0
a[1]() //0xc00004c088 1
a[2]() //0xc00004c090 2
}
func F() []func() {
b := make([]func(), 3, 3)
for i := 0; i < 3; i++ {
j := i
b[i] = func() {
fmt.Println(&j, j)
}
}
return b
}
每次 操作僅將匿名函數放入到數組中,但並未執行,並且引用的變量都是 i,隨着 i 的改變匿名函數中的 i 也在改變,所以當執行這些函數時,他們讀取的都是環境變量 i 最後一次的值。解決的方法就是每次複製變量 i 然後傳到匿名函數中,讓閉包的環境變量不相同。
5、
package main
import "fmt"
func main() {
fmt.Println(F())//2
}
func F() (r int) {
defer func() {
r++
}()
return 1
}
輸出結果為2,即先執行r=1 ,再執行r++。
6、遞歸函數
還有一種情況就是必須用都閉包,就是遞歸函數。
package main
import "fmt"
func F(i int) int {
if i <= 1 {
return 1
}
return i * F(i-1)
}
func main() {
var i int = 3
fmt.Println(i, F(i))// 3 6
}
7、斐波那契數列(Fibonacci)
這個數列從第3項開始,每一項都等於前兩項之和。
package main
import "fmt"
func fibonaci(i int) int {
if i == 0 {
return 0
}
if i == 1 {
return 1
}
return fibonaci(i-1) + fibonaci(i-2)
}
func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf("%d\n", fibonaci(i))
}
}
小結:
匿名函數和閉包其實是一回事兒,匿名函數就是閉包。匿名函數給編程帶來靈活性的同時也容易產生bug,在使用過程當中要多注意函數的參數,及可接受的參數的問題。
links
- 目錄