鍍金池/ 教程/ GO/ 6.1 介紹
4.7 strings 和 strconv 包
13.6 啟動外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯誤處理與測試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個例子:使用 Sorter 接口排序
?# 11.5 測試一個值是否實現(xiàn)了某個接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運行時異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實例
12.8 使用接口的實際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測試和基準(zhǔn)測試
6.8 閉包
4.9 指針
13.1 錯誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運行時(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對調(diào)
6.11 計算函數(shù)執(zhí)行時間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語言進(jìn)行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測試數(shù)據(jù))表驅(qū)動測試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調(diào)試器
4.5 基本類型和運算符
?# 11.8 第二個例子:讀和寫
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內(nèi)存緩存來提升性能
9.1 標(biāo)準(zhǔn)庫概述
12.6 用切片讀寫文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測試多返回值函數(shù)的錯誤
6.7 將函數(shù)作為參數(shù)
8.2 測試鍵值對是否存在及刪除元素
3.4 構(gòu)建并運行 Go 程序
2.1 平臺與架構(gòu)
5.3 switch 結(jié)構(gòu)

6.1 介紹

每一個程序都包含很多的函數(shù):函數(shù)是基本的代碼塊。

Go是編譯型語言,所以函數(shù)編寫的順序是無關(guān)緊要的;鑒于可讀性的需求,最好把 main() 函數(shù)寫在文件的前面,其他函數(shù)按照一定邏輯順序進(jìn)行編寫(例如函數(shù)被調(diào)用的順序)。

編寫多個函數(shù)的主要目的是將一個需要很多行代碼的復(fù)雜問題分解為一系列簡單的任務(wù)(那就是函數(shù))來解決。而且,同一個任務(wù)(函數(shù))可以被調(diào)用多次,有助于代碼重用。

(事實上,好的程序是非常注意DRY原則的,即不要重復(fù)你自己(Don't Repeat Yourself),意思是執(zhí)行特定任務(wù)的代碼只能在程序里面出現(xiàn)一次。)

當(dāng)函數(shù)執(zhí)行到代碼塊最后一行(} 之前)或者 return 語句的時候會退出,其中 return 語句可以帶有零個或多個參數(shù);這些參數(shù)將作為返回值(參考 第 6.2 節(jié))供調(diào)用者使用。簡單的 return 語句也可以用來結(jié)束 for 死循環(huán),或者結(jié)束一個協(xié)程(goroutine)。

Go 里面有三種類型的函數(shù):

除了main()、init()函數(shù)外,其它所有類型的函數(shù)都可以有參數(shù)與返回值。函數(shù)參數(shù)、返回值以及它們的類型被統(tǒng)稱為函數(shù)簽名。

作為提醒,提前介紹一個語法:

這樣是不正確的 Go 代碼:

func g()
{
}

它必須是這樣的:

func g() {
}

函數(shù)被調(diào)用的基本格式如下:

pack1.Function(arg1, arg2, …, argn)

Functionpack1 包里面的一個函數(shù),括號里的是被調(diào)用函數(shù)的實參(argument):這些值被傳遞給被調(diào)用函數(shù)的形參(parameter,參考 第 6.2 節(jié))。函數(shù)被調(diào)用的時候,這些實參將被復(fù)制(簡單而言)然后傳遞給被調(diào)用函數(shù)。函數(shù)一般是在其他函數(shù)里面被調(diào)用的,這個其他函數(shù)被稱為調(diào)用函數(shù)(calling function)。函數(shù)能多次調(diào)用其他函數(shù),這些被調(diào)用函數(shù)按順序(簡單而言)執(zhí)行,理論上,函數(shù)調(diào)用其他函數(shù)的次數(shù)是無窮的(直到函數(shù)調(diào)用棧被耗盡)。

一個簡單的函數(shù)調(diào)用其他函數(shù)的例子:

示例 6.1 greeting.go

package main

func main() {
    println("In main before calling greeting")
    greeting()
    println("In main after calling greeting")
}

func greeting() {
    println("In greeting: Hi!!!!!")
}

代碼輸出:

In main before calling greeting
In greeting: Hi!!!!!
In main after calling greeting

函數(shù)可以將其他函數(shù)調(diào)用作為它的參數(shù),只要這個被調(diào)用函數(shù)的返回值個數(shù)、返回值類型和返回值的順序與調(diào)用函數(shù)所需求的實參是一致的,例如:

假設(shè) f1 需要 3 個參數(shù) f1(a, b, c int),同時 f2 返回 3 個參數(shù) f2(a, b int) (int, int, int),就可以這樣調(diào)用 f1:f1(f2(a, b))。

函數(shù)重載(function overloading)指的是可以編寫多個同名函數(shù),只要它們擁有不同的形參與/或者不同的返回值,在 Go 里面函數(shù)重載是不被允許的。這將導(dǎo)致一個編譯錯誤:

funcName redeclared in this book, previous declaration at lineno

Go 語言不支持這項特性的主要原因是函數(shù)重載需要進(jìn)行多余的類型匹配影響性能;沒有重載意味著只是一個簡單的函數(shù)調(diào)度。所以你需要給不同的函數(shù)使用不同的名字,我們通常會根據(jù)函數(shù)的特征對函數(shù)進(jìn)行命名(參考 第 11.12.5 節(jié))。

如果需要申明一個在外部定義的函數(shù),你只需要給出函數(shù)名與函數(shù)簽名,不需要給出函數(shù)體:

func flushICache(begin, end uintptr) // implemented externally

函數(shù)也可以以申明的方式被使用,作為一個函數(shù)類型,就像:

type binOp func(int, int) int

在這里,不需要函數(shù)體 {}。

函數(shù)是一等值(first-class value):它們可以賦值給變量,就像 add := binOp 一樣。

這個變量知道自己指向的函數(shù)的簽名,所以給它賦一個具有不同簽名的函數(shù)值是不可能的。

函數(shù)值(functions value)之間可以相互比較:如果它們引用的是相同的函數(shù)或者都是 nil 的話,則認(rèn)為它們是相同的函數(shù)。函數(shù)不能在其它函數(shù)里面聲明(不能嵌套),不過我們可以通過使用匿名函數(shù)(參考 第 6.8 節(jié))來破除這個限制。

目前 Go 沒有泛型(generic)的概念,也就是說它不支持那種支持多種類型的函數(shù)。不過在大部分情況下可以通過接口(interface),特別是空接口與類型選擇(type switch,參考 第 11.12 節(jié))與/或者通過使用反射(reflection,參考 第 6.8 節(jié))來實現(xiàn)相似的功能。使用這些技術(shù)將導(dǎo)致代碼更為復(fù)雜、性能更為低下,所以在非常注意性能的的場合,最好是為每一個類型單獨創(chuàng)建一個函數(shù),而且代碼可讀性更強(qiáng)。

鏈接