golang 函式
在golang中,函式不僅可以用於封裝程式碼、分割功能、解耦邏輯,還可以化身為普通的值,在其他函式間傳遞、賦予變數、做型別判斷和轉換等等,就像切片和字典的值那樣。
函式值可以由此成為能夠被隨意傳播的獨立邏輯元件(或者說功能模組)。
demo:
package main import "fmt" type Printer func(content string) (n int, err error) func printToStd(content string) (byteNum int, err error) { return fmt.Println(content) } func main() { var p Printer p = printToStd p("something") }
demo中,先聲明瞭一個名叫Printer的函式型別。函式簽名(就是函式的引數列表和結果列表) 定義了可用來鑑別不同函式的那些特徵,同時定義了我們與函式的互動方式。
注意,各個引數和結果的名稱不能算作函式簽名的一部分,甚至對於結果宣告來說,沒有名稱都可以。
只要函式簽名是一致的,就可以說它們是一樣的函式,或者說實現了同一個函式型別的函式。
Go語言在語言層面支援了函數語言程式設計。
問:什麼是高階函式?
答:高階函式滿足兩個條件: 1.接受其他函式作為引數傳入; 2.把其他的函式作為結果返回。
只要滿足了其中一點,我們就可以說這個函式是一個高階函式。高階函式也是函數語言程式設計中的重要概念和特徵。
demo1:1.接受其他函式作為引數傳入
編寫calculate函式來實現兩個整數間的加減乘除運算,但是希望兩個整數和具體的操作都有該函式呼叫方給出。
type operate func (x, y int) int func calculate(x int, y int, op operate) { if op == nil { return 0, errors.New("invalid operation") } return op(x,y), nil }
calculate函式的其中一個引數是operate型別的,而且後者就是一個函式型別。在呼叫calculate函式的時候,我們需要傳入一個operate型別的函式值。
只要傳入的函式與operate的簽名一致,並且實現得當就可以了
op := func(x, y int) { return x + y }
demo2:2.把其他的函式作為結果返回。
x,y = 56, 78 add := genCalculator(op) result, err = add(x, y) fmt.Println("The result : %d (error: %v)\n",result, err) funcgenCalculator(op operate ) func(int,int) (int,error) { returnfunc(x int, y int) { if op == nil { return 0, errors.New("invalid operation") } return op(x,y), nil } }
什麼是閉包?
在一個函式中存在對外來識別符號的引用(既不代表當前函式的任何引數或結果,也不是函式內部宣告的,是直接從外邊拿過來的),這中變數叫自由變數 。閉包 就是因為引用了自由變數,而呈現出一種“”不確定“的狀態,也叫“”開放”狀態。也就是說,它的內部邏輯並不是完整的,有一部分邏輯需要這個自由變數參與完成,而後者代表了什麼在閉包函式被定義的時候確實未知的。
在demo2中,op 就是自由變數 ,當Go語言讀取到if op == nil 這一行時會試圖去尋找op 所代表的的東西,它會發現op 代表的是genCalculator函式的引數 ,然後,它會把這兩者聯絡起來,這時可以說,自由變數op 被捕獲了。
閉包的意義?
表面上看我們只是延遲了實現一部分程式邏輯或功能而已。但實際上,我們是動態地生成那部分邏輯功能。我們可以藉此在程式執行的過程中,根據需要生成功能不同的函式,繼而影響後續的程式行為。這與GoF設計模式中的模板方法有著異曲同工之妙。
傳入函式的引數值後來怎麼樣了?
demo3:
package main import "fmt" func main() { array1 := [3]string{"a", "b", "c" } fmt.Printf("The array: %v\n",array1) array2 := modifyArray(array1) fmt.Println("The modified array: %v\n", array2) fmt.Println("The original array: %v\n", array1) } func modifyArray(a [3]string) [3]string { a[1] = "x" return a }
答:原陣列array1不會改變。所有傳給函式的引數值都會被複制,函式在其內部使用的並不是引數值的原值,而是它的副本。注意:對於引用型別:比如:切片、字典、通道,複製的是它們本身麼不是它們引用的底層資料,所以當改變時會造成底層資料的改變。