《Go程序設計語言》第五章總結

第五章:函數
	·當函數存在返回值的時候必須顯式地以return語句結束,除非函數明確不會走完整個流程(比如一個for死循環)
	·函數的類型 
		稱爲函數簽名,當一個函數擁有相同的形參列表和返回列表時,認爲這兩個函數的類型或簽名是相同的。形參和返回參數的名稱不會影響函數的類型
	·實參是按值傳遞的,所以函數接收到的是沒有實參的副本,修改形參的變量並不會影響到實參的值。
		但是,如果提供的實參是引用類型,比如 指針,slice,map,函數或者通道,那麼形參變量就有可能間接的修改實參變量
	·有些函數沒有函數體,說明不是用go語言實現的,eg
		package math
		func Sin(x float64) float64  //使用彙編語言實現
	·錯誤
		·習慣上將錯誤值作爲最後一個結果返回,如果錯誤只有一個情況,結果通常設置爲布爾類型
		·與其他語言不同,go語言通過使用普通變量的值而非異常來報告錯誤。儘管go有異常機制,但是go語言的異常是針對bug導致的預料外的錯誤
		·go程序使用通常的控制流機制(比如if和return語句)也對錯誤
		·錯誤處理策略:
			當函數調用返回一個錯誤時,調用者應該負責檢查錯誤並採取合適的處理應對
			·首先最常見的是將錯誤傳遞下去,在子例程中發生的錯誤通過返回值返回給主例程,這樣在主獲得錯誤並進行判斷處理
		·log.Fatalf("Size is down: %v\n",err) //以這種方式處理錯誤,它默認會將時間和日期作爲前綴添加到錯誤消息前
		·當讀取文件到結束的位置,io包會返回 EOF 錯誤
			package io
			import "errors"
			var EOF = errors.New("EOF")
			eg: 一段讀取文件的錯誤處理 (僞代碼)
				for {
					r,_,err := in.read()
					if (err == io.EOF) {
						break
					}
					if err != nil {
						return fmt.Errorf("read failed: %v",err)
					}
				}
				
	·函數變量:
		var f func(int) int
		func square(n int) int {return n * n}
		func negative(n int) int {return -n}
		func product(m,n int) int {return  m * n}
		func main()  {
			f := square //f的類型已經確定了
			fmt.Println(f(3))
			
			/**
			negative 和 square 的函數類型是一致的,所以f可以完成賦值
			函數的類型 是由形參列表和返回值列表確定的
			 */
			f = negative
			fmt.Println(f(3))
		
			//f = product //編譯錯誤: 不能把類型 func(int,int) int 賦值 給 func (int) int
			//fmt.Println(f(3))
			
			g := product
			fmt.Println(g(2,3))
		}
	
	·不能調用一個空的函數變量
		var f func(int) int //
		func main()  {
			fmt.Println(f(3))
		}
	·defer : 正確使用defer是在成功獲得資源之後
		resp,err := http.Get(url)
		if err != nil {
			return err
		}
		defer resp.Body.Close() //確定了成功獲取資源之後釋放資源
	
	·可以使用改方法來測試一個函數的調用時間
		func main()  {
			defer f1()() //注意這裏是兩個括號
			time.Sleep(time.Second * 5)
		}
		func f1() func()  {
			start := time.Now() //這個不是延遲執行
			return func() {  //延遲執行的是返回的這個函數
				fmt.Println(time.Since(start))   //可以計算函數的調用時間
			}
		}
	·延遲函數可以獲取和改變返回值
		func main()  {
			fmt.Println(double(5))
		}
		func double(x int) (result int) {
			defer func() {
				fmt.Println(x,result)
				result += 100 //延遲函數不到函數的最後一刻是不會執行的
			}()
			return x + x
		}
	·注意延時函數在循環裏面的使用
		for _,filename := range filenames {
			f, err := os.Open(filename)
			if err != nil {
				return err
			}
			defer f.Close //這樣f.Close根本沒有在每次循環的時候關閉,會用盡資源
		}
		解決方案是:將for循環放到一個函數裏面,這樣函數結束就會執行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() //這樣每次執行都會調用到f.Close
			}
			
	·後defer的先執行,相當於一個棧

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章