一、介紹
特點:
- 在go語言中,函式是一等公民,可以作為引數,變數,返回值來使用
- 高階函式(在函式中定義函式,傳入函式,返回函式)
- 閉包
附加:正統式函數語言程式設計
1.不可變性:不能有狀態,只能有常量和函式
2.函式只有一個引數
對於正統式函數語言程式設計(較為複雜,閱讀性不高)我們不做太多介紹
好下面我們看一個例子:
func adder() func(v int) int{
sum := 0
return func(v int) int{
sum += v
return sum
}
}
這裡我們寫了一個求和的函式(這裡將匿名函式func(v int)作為返回值,並將匿名函式func的定義直接寫在函式adder的return後面)。
接著看下面:
我們在main函式中測試一下
package main
import "fmt"
func adder() func(v int) int{
sum := 0
return func(v int) int{
sum += v
return sum
}
}
func main(){
a := adder
fmt.Println(a(1))
fmt.Println(a(2))
fmt.Println(a(9))
}
//輸出結果為:1、3、12
看到這裡是不是有點模糊了,沒事我來解釋一遍:
首先 a := adder 是將adder的返回值給到a
再仔細看adder的返回值是一個匿名函式,所以a就相當於函式
func(v int) int{}的例項,a(1) 、 a(2) 、a(3)自然就能理解了,就是對應v
是不是不明白輸出結果,下面就來介紹一下閉包
閉包:
定義:Go語言中閉包是引用了自由變數的函式,被引用的自由變數和函式一同存在,即使已經離開了自由變數的環境也不會被釋放或者刪除,在閉包中可以繼續使用這個自由變數,因此,簡單的說:函式 + 引用環境 = 閉包
我的理解:就是指函式在第一次呼叫時的返回值,返回後,其返回值不會立即被刪除(儲存在某個地方),當下一次呼叫該函式時會將上一次的返回值作為這次呼叫的引數;例如上面的求和函式:
func adder() func(v int) int{
sum := 0
return func(v int) int{
sum += v
return sum
}
}
func main(){
a := adder
fmt.Println(a(1)) //第一次呼叫時sum = 0 , sum = 0 + 1,返回值為1
fmt.Println(a(2)) //第二次呼叫時sum = 1 ,sum = 1 +2,返回值為3
fmt.Println(a(9)) //第三次呼叫時sum = 9,sum = 3 + 9,返回值為12
}
這是不是看懂了閉包
func adder() func(v int) int{
sum := 0
return func(v int) int{
sum += v
return sum
}
func main() {
a := adder()
for i := 0; i <= 5; i++{
fmt.Println(a1(i))
}
//輸出結果為:0、1、3、6、10、15
二、函數語言程式設計的應用
1、斐波那契數列
數列:0、1、1、2、3、5、8、13、21……
前兩個數之和是第三個數,0+1 =1、1+1=2、1+2=3、2+3=5、3+5=8、5+8=12……
這裡我們需要使用函數語言程式設計的模式將其實現
package main
func fibonacci() func() int{
a :=0, b := 1
return func() int{
a , b = b, a+b
return a
}
}
func main(){
f := fibonacci()
fmt.Println(f()) //1
fmt.Println(f()) //1
fmt.Println(f()) //2
fmt.Println(f()) //3
fmt.Println(f()) //5
fmt.Println(f()) //8
}
斐波那契數列生成器:
這裡我們需要像讀取檔案一樣,就需要實現read的介面
我們將列印函式拿過來:
func printContentFile(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() { //
println(scanner.Text())
}
}
x想要直接呼叫printContentFile()自動生成斐波那契數,就必須實現read介面,定義一個type:函式。使其實現Reader介面。
//函式實現介面
//定義一個函式,使用type修飾。可以實現介面,也就是說只要是被type修飾的東西都可以實現介面。
type IntGen func() int
//實現read介面
func (g IntGen) Read(p []byte) (n int, err error) {
next := g()
if next > 10000 { //這裡因為不設定退出會一直列印下去,所以做了限制
return 0, io.EOF
}
s := fmt.Sprintf("%d\n", next)
return strings.NewReader(s).Read(p)
}
函式返回為fibonacci()
/**
函式生成斐波那契數列(每個數字都是由前兩個數字相加得到)
*/
func fibonacci() IntGen {
a, b := 0, 1
return func() int {
//在這裡,生成關鍵
// 1 1 2 3 5 8
// a b
// a b
a, b = b, a+b
return a
}
}
最後呼叫:
func main() {
fun := fibonacci()
printContentFile(fun)
}
列印結果:
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
從這個例子中可以發現,函式還可以實現介面,使用在go語言中函式是一等公民
2、函數語言程式設計實現中序遍歷 + 多業務
package tree1
import "fmt"
//結構體Node
type Node struct{
Value int
Left, Right *Node
}
//列印
func (node *Node)Println(){
print(node.Value)
}
func (node *Node)Traverse(){ //中序遍歷+列印
node.TraverseFun( func(*Node){
node.print()
})
fmt.Println()
}
func (node *Node)TraverseFun(f func(*node){ //中序遍歷+……
if node == nil{
return
}
node.Left.TraverseFun(f)
f(node)
node.Right.TraverseFun(f)
}
func main() {
//向樹中新增資料
root := tree1.Node{Value:0}
root.Left = &tree1.Node{1, nil, nil}
root.Right = &tree1.Node{2, nil,nil}
root.Left.Left = &tree1.Node{3, nil,nil}
root.Left.Right = &tree1.Node{4, nil, nil}
root.Right.Left = &tree1.Node{5, nil,nil}
root.Right.Left = &tree1.Node{6, nil, nil}
//中序遍歷
root.Traverse()
TraverseCount := 0 //列印資料個數
root.TraverseFunc(func(node *tree1.Node) {
TraverseCount++
})
fmt.Println(TraverseCount)
}
看到是沒有問題的。且比之前的Traverse
更加靈活了,在中序遍歷是可以做很多東西了
本作品採用《CC 協議》,轉載必須註明作者和本文連結