2012-05-19 翻譯自這里, 對原文有所擴展, 也有所刪減.
go是函數式編程語言嗎?
不是, 當然不是.
那么, go提供函數嗎?
是的, 當然, 大多數編程語言都提供函數, go也不例外. 不相信嗎? 我會用代碼讓你閉嘴:
func SayHello() { fmt.Println("Hello") }
看見了吧. go使用關鍵字func定義函數, 並在函數體中編寫函數邏輯.
go函數可以接受參數嗎?
嗯, 我又看到一個白痴的問題, 呵呵. 哦, 我懂了, 也許是我的SayHello函數給大家造成了錯覺, 我會改造我的代碼:
func SayHelloToSomeone(name string) { fmt.Println("Hello " + name + ".") }
函數SayHelloToSomeone接受一個string類型的參數name.
go函數是否可以有返回值?
是的, 是的, 是的! 就像數學意義上的函數一樣, go函數可以返回給調用者一些東西. 為了演示這一點, 我將重新編寫一個函數:
func GetGreeting (name string) string { greeting := "Hello " + name + "."
return greeting } // test
greeting := GetGreeting("Bob") fmt.Println(greeting) //outputs "Hello Bob."
當然這沒有什么, 其他語言也可以做到. 但是, 准備好接受驚喜了嗎?
go函數的返回值與其他類C語言有些不同, 比如, 你可以為返回值指定名稱. 這帶來至少2個好處:
1. 不需要在函數體中為返回值定義變量.
2. 無需在return語句后加上返回值. go會自動將返回值加上.
func GetGreeting (name string) (greeting string) { greeting = "Hello " + name + "."
return }
GetGreeting函數為其返回值指定了名稱: greeting. 在函數體中就可以直接使用greeting變量了, greeting其實就相當於函數中定義的一個局部變量. 而且如你所見, GetGreeting函數的return語句后面沒有加上返回值, 因為go會自動將greeting變量的值返回給調用者.
go函數可以返回多個值嗎?
這, 這, 你是異想天開嗎? 不過強大的go函數能夠做到這一點, 哈哈. 要知道可以有多個返回值的函數可以避免很多丑陋的代碼, 下面是示例:
type Stack struct { pos int data [10]int } func (s *Stack) Pop() (value int, ok bool) { if s.pos > 0 { s.pos-- ok = true value = s.data[s.pos] return } ok = false
return }
代碼中首先定義了Stack類型, 並提供了Pop函數. Pop函數返回2個值: value和ok, 分別代表pop的值和pop操作是否成功.
go函數可以接受一個函數作為參數嗎?
嗯, 我想你終於開始集中注意力了.
如果你是一個醫生, 你是否會對每次都需要向你的病人SayHello感到厭煩? 沒關系, go可以幫助你. 首先需要定義新的數據結構:
type TormentList struct { patients []string } // 將[]string(string數組)包裝成TormentList類型的指針
func NewTormentList(people []string) *TormentList { return &TormentList{people} }
接下來, 讓我們悄悄給TormentList類型增加Map方法:
func (g *TormentList) Map(f func(string)) { // 遍歷g.patients, 為其每個value調用f方法
for _, val := range(g.patients) { f(val) } }
Map方法接受f函數作為其輸入參數, f函數接受一個string類型的值. 遍歷TormentList的病人, 並為每個病人調用f函數.
現在已經做好了一切准備, 剩下的就是測試了:
patients := []string{"Anand", "David", "Ivan", "JoJo", "Jin", "Mon", "Peter", "Sachin"} gl := NewTormentList(patients) // 還記得上面定義的SayHelloToSomeone函數吧?
gl.Map(SayHelloToSomeone) /* outputs the following: Hello Anand. Hello David. Hello Ivan. Hello JoJo. Hello Jin. Hello Mon. Hello Peter. Hello Sachin. */
go函數的返回值可以是函數嗎?
讓我們先考慮一個現實問題: 假如你擁有一份吃過壽司的人的清單, 你是否能夠根據人名確定他是否在清單上? 這是個很簡單的問題, 你只需遍歷清單. 嗯, 如果你go的功底很弱, 不知道怎么遍歷清單那怎么辦? 沒關系, 我會給你提供一個刷選器:
func Screen(patients []string) func(string) bool { // 定義匿名函數並返回
return func(name string) bool { for _, soul := range patients { if soul == name { return true } } return false } }
Screen方法會將刷選的函數返回給調用方, 這樣你就可以不用懂怎么去遍歷清單了, 你只需調用我返回給你的函數就可以:
// 吃過壽司的人的清單
those_who_bought_sushi := []string{"Anand", "JoJo", "Jin", "Mon", "Peter", "Sachin"} // 得到刷選器函數
bought_sushi := Screen(those_who_bought_sushi) // 調用刷選器函數就可以知道某人是否在清單上
fmt.Println(bought_sushi("Anand")) // true
fmt.Println(bought_sushi("Alex")) // false