《Go 語言程序設計》在線閱讀地址:https://yar999.gitbooks.io/go...html
func name(parameter-list) (result-list) { body }
形式參數列表描述了函數的參數名以及參數類型。這些參數做爲局部變量,其值由參數調用者提供。返回值也能夠像形式參數同樣被命名,在這種狀況下,每一個返回值被聲明成一個局部變量,並初始化爲其類型的零值。git
func first(x int, _ int) int { return x }
links, err := findLinks(url)
若是某個值不被使用,能夠將其分配給blank identifier:golang
links, _ := findLinks(url) // errors ignored
// CountWordsAndImages does an HTTP GET request for the HTML // document url and returns the number of words and images in it. func CountWordsAndImages(url string) (words, images int, err error) { resp, err := http.Get(url) if err != nil { return } doc, err := html.Parse(resp.Body) resp.Body.Close() if err != nil { err = fmt.Errorf("parsing HTML: %s", err) return } words, images = countWordsAndImages(doc) return } func countWordsAndImages(n *html.Node) (words, images int) { /* ... */ }
按照函數聲明中返回值列表的次序,返回全部的返回值,在上面的例子中,每個return語句等價於:web
return words, images, err
return 0,0,err
(Go會將返回值 words和images在函數體的開始處,根據它們的類型,將其初始化爲0),最後一處return等價於 return words,image,nil
。基於以上緣由,不宜過分使用bare return。resp, err := http.Get(url)
Error
函數或者輸出函數得到字符串類型的錯誤信息。fmt.Println(err) fmt.Printf("%v", err)
func square(n int) int { return n * n } func negative(n int) int { return -n } func product(m, n int) int { return m * n } f := square fmt.Println(f(3)) // "9" f = negative fmt.Println(f(3)) // "-3" fmt.Printf("%T\n", f) // "func(int) int" f = product // compile error: can't assign func(int, int) int to func(int) int
var f func(int) int f(3) // 此處f的值爲nil, 會引發panic錯誤
var f func(int) int if f != nil { f(3) }
可是函數值之間是不可比較的,也不能用函數值做爲map的key。編程
函數字面量容許咱們在使用函數時,再定義它。經過這種技巧,咱們能夠改寫以前對strings.Map的調用:數組
strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000")
更爲重要的是,經過這種方式定義的函數能夠訪問完整的詞法環境(lexical environment),這意味着在函數中定義的內部函數能夠引用該函數的變量。安全
// squares返回一個匿名函數。 // 該匿名函數每次被調用時都會返回下一個數的平方。 func squares() func() int { var x int return func() int { x++ return x * x } }
經過這個例子,咱們看到變量的生命週期不禁它的做用域決定:squares返回後,變量x仍然隱式的存在於f中。服務器
var visitAll func(items []string) visitAll = func(items []string) { ...... visitAll(m[item]) ...... }
不然會出現編譯錯誤
visitAll := func(items []string) { // ... visitAll(m[item]) // compile error: undefined: visitAll // ... }
在聲明可變參數函數時,須要在參數列表的最後一個參數類型以前加上省略符號「...」,這表示該函數會接收任意數量的該類型參數。網絡
func sum(vals...int) int { total := 0 for _, val := range vals { total += val } return total }
sum函數返回任意個int型參數的和。在函數體中,vals被看做是類型爲[] int
的切片。sum能夠接收任意數量的int型參數:編程語言
fmt.Println(sum()) // "0" fmt.Println(sum(3)) // "3" fmt.Println(sum(1, 2, 3, 4)) // "10"
values := []int{1, 2, 3, 4} fmt.Println(sum(values...)) // "10" // fmt.Println(sum(1, 2, 3, 4))
...int
型參數的行爲看起來很像切片類型,但實際上,可變參數函數和以切片做爲參數的函數是不一樣的。func f(...int) {} func g([]int) {} fmt.Printf("%T\n", f) // "func(...int)" fmt.Printf("%T\n", g) // "func([]int)"
func errorf(linenum int, format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "Line %d: ", linenum) fmt.Fprintf(os.Stderr, format, args...) fmt.Fprintln(os.Stderr) } linenum, name := 12, "count" errorf(linenum, "undefined: %s", name) // "Line 12: undefined: count"
...interfac{}
表示函數在format
參數後能夠接收任意個任意類型的參數。interface{}
會在後面介紹。
package ioutil func ReadFile(filename string) ([]byte, error) { f, err := os.Open(filename) if err != nil { return nil, err } defer f.Close() return ReadAll(f) }
var mu sync.Mutex var m = make(map[string]int) func lookup(key string) int { mu.Lock() defer mu.Unlock() return m[key] }
func bigSlowOperation() { defer trace("bigSlowOperation")() // don't forget the extra parentheses // ...lots of work… time.Sleep(10 * time.Second) // simulate slow operation by sleeping } func trace(msg string) func() { start := time.Now() log.Printf("enter %s", msg) return func() { log.Printf("exit %s (%s)", msg,time.Since(start)) } }
每一次bigSlowOperation被調用,程序都會記錄函數的進入,退出,持續時間。(咱們用time.Sleep模擬一個耗時的操做)
$ go build gopl.io/ch5/trace $ ./trace 2015/11/18 09:53:26 enter bigSlowOperation 2015/11/18 09:53:36 exit bigSlowOperation (10.000589217s)
func double(x int) (result int) { defer func() { fmt.Printf("double(%d) = %d\n", x,result) }() return x + x } _ = double(4) // Output: // "double(4) = 8"
func triple(x int) (result int) { defer func() { result += x }() return double(x) } fmt.Println(triple(4)) // "12"
for _, filename := range filenames { f, err := os.Open(filename) if err != nil { return err } defer f.Close() // NOTE: risky; could run out of file descriptors // ...process f… }
一種解決方法是將循環體中的文件操做和defer語句移至另一個函數。在每次循環時,調用這個函數。
for _, filename := range filenames { if err := doFile(filename); err != nil { return err } } func doFile(filename string) error { f, err := os.Open(filename) if err != nil { return err } defer f.Close() // ...process f… }
func Parse(input string) (s *Syntax, err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("internal error: %v", p) } }() // ...parser... }
func soleTitle(doc *html.Node) (title string, err error) { type bailout struct{} defer func() { switch p := recover(); p { case nil: // no panic case bailout{}: // "expected" panic err = fmt.Errorf("multiple title elements") default: panic(p) } }() forEachNode(doc, func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil { if title != "" { panic(bailout{}) // multiple titleelements } title = n.FirstChild.Data } }, nil) if title == "" { return "", fmt.Errorf("no title element") } return title, nil }