Golang理解-匿名函數


匿名函數


匿名函數(英語:Anonymous Function)在計算機編程中是指一類無需定義標識符(函數名)的函數子程序,普遍存在於多種編程語言中。---wikipedia

Golang是支持匿名函數的,即在需要使用函數時,再定義函數,匿名函數沒有函數名,只有函數體,函數可以被作為一種類型被賦值給函數類型的變量,匿名函數往往以變量方式被傳遞

匿名函數經常被用於實現回調函數,閉包等.

匿名函數定義

匿名函數的定義就是: 沒有名字的普通函數

func (參數列表) (返回值列表) {
	函數體
}

匿名函數的調用

1 在定義時調用匿名函數

匿名函數可以在聲明后直接調用; 例如:

package main

import "fmt"

func main() {
  	// 定義匿名函數並賦值給f變量
    f := func(data int) {
      fmt.Println("hello", data)
    }
  // 此時f變量的類型是func(), 可以直接調用
    f(100)
}

匿名函數的用途非常廣泛,匿名函數本身是一種值,可以方便的保存在各種容器中實現回調函數和操作封裝

2 匿名函數做回調函數

回調函數,或簡稱回調(Callback 即call then back 被主函數調用運算后會返回主函數),是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼引用

匿名函數作為回調函數的設計在go語言的系統包中是很長見的;strings包中就有這種實現:

func TrimFunc(s string, f func(rune) bool) string {
		return TrimRightFunc(TrimLeftFunc(s, f), f)
}

下面我們自己來寫個demo,領會回調函數的特點:

package main

import "fmt"

/*
	目標:
		對切片的遍歷操作,遍歷中訪問每個元素的操作使用匿名函數來實現. 
		用戶傳入不同的匿名函數體可以實現對元素的遍歷操作
*/

// 遍歷切片中每個元素,通過給定的函數進行元素訪問
func visit(list []int, f func(int)) {
	for _, value := range list {
		f(value)
	}
} 

func main() {
	l := []int{1,2,3}
	// 使用匿名函數打印切片的內容
	visit(l, func(value int) {
		fmt.Println(value)
	})
}

匿名函數實現封裝

封裝(英語:Encapsulation)是指,一種將抽象性函數接口的實現細節部分包裝、隱藏起來的方法。同時,它也是一種防止外界調用端,去訪問對象內部實現細節的手段,這個手段是由編程語言本身來提供的.

下面這段代碼將匿名函數作為map的鍵值,通過命令函數參數動態調用匿名函數;

package main

import (
	"fmt"
	"flag"
)


// 使用flag包解析命令行傳入的參數
// flag包中解析出來的參數是 *string 類型的指針變量, 在調用值時不能直接調用,使用*取值
var skillParm = flag.String("skill", "", "skill to perform")

func main() {
	// 解析參數; 解析后skillParm將執行命令行傳入的值
	flag.Parse()

	// 定義一個字符串映射到func()的map,然后填充這個map
	var skill = map[string]func(){
      "fire": func(){
          fmt.Println("chicken fire")
      },
      "run": func() {
          fmt.Println("soldier run")
      },
      "fly": func(){
          fmt.Println("angle fly")
      },
	}
	// 如果傳入的值存在就調用map的值(這里的值是一個匿名函數)進行打印
	if f, ok := skill[*skillParm]; ok {
			f()
	} else {
			fmt.Println("skill not found")
	}

}

函數實現接口

接口在go語言中是被廣泛使用的,江湖有種說法"go是面向接口編程". 那么如果使用函數來實現和接口一樣的功能呢?

函數和其他類型一樣都屬於“一等公民”,其他類型能夠實現接口,函數也可以實現

我們來對比函數和結構體在實現接口上的異同點:

1 結構體實現接口

我們要實現的功能是: 這個接口需要實現Call()方法,調用會傳入一個interface{}類型的變量,這種類型的變量表示任意類型的值.

package main

import (
	"fmt"
)

// 調用器接口,定義一個接口,實現Call()方法
type Invoker interface{
		Call(interface{})
}

// 定義結構體類型, 未定義任何成員,能夠接收任意類型的數據
type Struct struct{}

// 實現Invoker的Call方法
func (s *Struct) Call(p interface{}) {
		fmt.Println("from struct", p)
}

func main() {
    // 聲明接口變量,將定義的Struct類型實例化,並傳入接口中進行調用
    var invoker Invoker

    // 實例話結構體
    s := new(Struct)  // <==> s := &Struct

    // 將實例化的結構體賦值給接口
    invoker = s

    // 使用接口調用實例化結構體的方法Strct.Call()方法
    invoker.Call("hello")
}

2 函數實現接口

函數實現接口: 函數的聲明不能直接實現接口,需要將函數定義為類型后,使用類型實現結構體, 當類型方法被調用時,還需要調用函數本體

package main

import (
	"fmt"
)

// 調用器接口
type Invoker interface{
		Call(interface{})
}

// 函數定義為類型, 將func(interface{})定義為FuncCaller類型; 
// 這里只是定義了函數類型,需要函數本身進行邏輯處理. 
// FuncCaller無需被實例化,只需要將函數轉換為FuncCaller類型即可,
// 函數來源可以是命名函數、匿名函數或閉包
type FuncCaller func(interface{})

// 實現Invoker的Call方法
func (f FuncCaller) Call(p interface{}) {
    // 調用函數本體, FuncCaller的Call()方法被調用與func(interface{})無關,
  	// 還需要手動調用函數本體
    f(p)
}
func main(){
    // 聲明接口變量
    var invoker Invoker

    // 將匿名函數轉為FuncCaller類型,在賦值給接口
    invoker = FuncCaller(func(v interface{}) {
      fmt.Println("from function", v)
    })

    // 使用接口調用FuncCaller.Call,內部會調用函數本體
    invoker.Call("hello")
}

系統包中的例子

在Go語言中,系統的某些包也是用函數來實現接口,例如HTTP包中就有的函數實現接口的例子;

HTTP包中包含有Handler接口定義,代碼如下:

type Handler interface {
	ServeHTTP(Response Writer, *Request)
}

Handler 用於定義每個http的請求和相應的處理過程, 同時,也可以使用處理函數實現接口,定義如下:

type HandlerFunc func(ResponseWriter, *Request)

func(f HandlerFunc) ServeHTTP(W ResponseWriter, r *Request) {
	f(w, r)
}

要使用閉包實現默認的HTTP請求處理,可以使用http.HandleFunc()函數, 函數定義如下:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

而 DefaultServeMux 是ServeMux結構,擁有HandlerFunc()方法,定義如下:

func (mux *ServeMux) HandlerFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handler(pattern, HandlerFunc(handler))
}

上面代碼將外部傳入的函數handler()轉為HandlerFunc類型,HandlerFunc類型實現了Handler的ServeHTTP方法,底層可以同時使用各種類型來實現Handler接口進行處理.

參考

Go語言從入門到進階實戰 - 徐波


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM