原文鏈接:https://chai2010.cn/advanced-go-programming-book/ch1-basic/ch1-01-genesis.html
Go 語言最初由 Google 公司的 Robert Griesemer、Ken Thompson 和 Rob Pike 三個大牛于 2007 年開始設(shè)計發(fā)明,設(shè)計新語言的最初的洪荒之力來自于對超級復(fù)雜的 C++11 特性的吹捧報告的鄙視,最終的目標是設(shè)計網(wǎng)絡(luò)和多核時代的 C 語言。到 2008 年中期,語言的大部分特性設(shè)計已經(jīng)完成,并開始著手實現(xiàn)編譯器和運行時,大約在這一年 Russ Cox 作為主力開發(fā)者加入。到了 2009 年,Go 語言已經(jīng)逐步趨于穩(wěn)定。同年 9 月,Go 語言正式發(fā)布并開源了代碼。
Go 語言很多時候被描述為“類 C 語言”,或者是“21 世紀的 C 語言”。從各種角度看,Go 語言確實是從 C 語言繼承了相似的表達式語法、控制流結(jié)構(gòu)、基礎(chǔ)數(shù)據(jù)類型、調(diào)用參數(shù)傳值、指針等諸多編程思想,還有徹底繼承和發(fā)揚了 C 語言簡單直接的暴力編程哲學(xué)等。圖1-1是《Go語言圣經(jīng)》中給出的 Go 語言的基因圖譜,我們可以從中看到有哪些編程語言對 Go 語言產(chǎn)生了影響。
圖 1-1 Go 語言基因族譜
首先看基因圖譜的左邊一支??梢悦鞔_看出 Go 語言的并發(fā)特性是由貝爾實驗室的 Hoare 于 1978 年發(fā)布的 CSP 理論演化而來。其后,CSP 并發(fā)模型在 Squeak/NewSqueak 和 Alef 等編程語言中逐步完善并走向?qū)嶋H應(yīng)用,最終這些設(shè)計經(jīng)驗被消化并吸收到了 Go 語言中。業(yè)界比較熟悉的 Erlang 編程語言的并發(fā)編程模型也是 CSP 理論的另一種實現(xiàn)。
再看基因圖譜的中間一支。中間一支主要包含了 Go 語言中面向?qū)ο蠛桶匦缘难莼瘹v程。Go 語言中包和接口以及面向?qū)ο蟮忍匦詣t繼承自 Niklaus Wirth 所設(shè)計的 Pascal 語言以及其后所衍生的相關(guān)編程語言。其中包的概念、包的導(dǎo)入和聲明等語法主要來自于 Modula-2 編程語言,面向?qū)ο筇匦运峁┑姆椒ǖ穆暶髡Z法等則來自于 Oberon 編程語言。最終 Go 語言演化出了自己特有的支持鴨子面向?qū)ο竽P偷碾[式接口等諸多特性。
最后是基因圖譜的右邊一支,這是對 C 語言的致敬。Go 語言是對 C 語言最徹底的一次揚棄,不僅僅是語法和 C 語言有著很多差異,最重要的是舍棄了 C 語言中靈活但是危險的指針運算。而且,Go 語言還重新設(shè)計了 C 語言中部分不太合理運算符的優(yōu)先級,并在很多細微的地方都做了必要的打磨和改變。當然,C 語言中少即是多、簡單直接的暴力編程哲學(xué)則被 Go 語言更徹底地發(fā)揚光大了(Go 語言居然只有 25 個關(guān)鍵字,sepc 語言規(guī)范還不到 50 頁)。
Go 語言其它的一些特性零散地來自于其他一些編程語言;比如 iota 語法是從 APL 語言借鑒,詞法作用域與嵌套函數(shù)等特性來自于 Scheme 語言(和其他很多編程語言)。Go 語言中也有很多自己發(fā)明創(chuàng)新的設(shè)計。比如 Go 語言的切片為輕量級動態(tài)數(shù)組提供了有效的隨機存取的性能,這可能會讓人聯(lián)想到鏈表的底層的共享機制。還有 Go 語言新發(fā)明的 defer 語句(Ken 發(fā)明)也是神來之筆。
作為 Go 語言標志性的并發(fā)編程特性則來自于貝爾實驗室的 Tony Hoare 于 1978 年發(fā)表的鮮為外界所知的關(guān)于并發(fā)研究的基礎(chǔ)文獻:順序通信進程(communicating sequential processes ,縮寫為 CSP)。在最初的 CSP 論文中,程序只是一組沒有中間共享狀態(tài)的平行運行的處理過程,它們之間使用管道進行通信和控制同步。Tony Hoare 的 CSP 并發(fā)模型只是一個用于描述并發(fā)性基本概念的描述語言,它并不是一個可以編寫可執(zhí)行程序的通用編程語言。
CSP 并發(fā)模型最經(jīng)典的實際應(yīng)用是來自愛立信發(fā)明的 Erlang 編程語言。不過在 Erlang 將 CSP 理論作為并發(fā)編程模型的同時,同樣來自貝爾實驗室的 Rob Pike 以及其同事也在不斷嘗試將 CSP 并發(fā)模型引入當時的新發(fā)明的編程語言中。他們第一次嘗試引入 CSP 并發(fā)特性的編程語言叫 Squeak(老鼠的叫聲),是一個用于提供鼠標和鍵盤事件處理的編程語言,在這個語言中管道是靜態(tài)創(chuàng)建的。然后是改進版的 Newsqueak 語言(新版老鼠的叫聲),新提供了類似 C 語言語句和表達式的語法,還有類似 Pascal 語言的推導(dǎo)語法。Newsqueak 是一個帶垃圾回收的純函數(shù)式語言,它再次針對鍵盤、鼠標和窗口事件管理。但是在 Newsqueak 語言中管道已經(jīng)是動態(tài)創(chuàng)建的,管道屬于第一類值、可以保存到變量中。然后是 Alef 編程語言(Alef 也是 C 語言之父 Ritchie 比較喜愛的編程語言),Alef 語言試圖將 Newsqueak 語言改造為系統(tǒng)編程語言,但是因為缺少垃圾回收機制而導(dǎo)致并發(fā)編程很痛苦(這也是繼承 C 語言手工管理內(nèi)存的代價)。在 Aelf 語言之后還有一個叫 Limbo 的編程語言(地獄的意思),這是一個運行在虛擬機中的腳本語言。Limbo 語言是 Go 語言最接近的祖先,它和 Go 語言有著最接近的語法。到設(shè)計 Go 語言時,Rob Pike 在 CSP 并發(fā)編程模型的實踐道路上已經(jīng)積累了幾十年的經(jīng)驗,關(guān)于 Go 語言并發(fā)編程的特性完全是信手拈來,新編程語言的到來也是水到渠成了。
圖1-2展示了 Go 語言庫早期代碼庫日志可以看出最直接的演化歷程(Git 用 git log --before={2008-03-03} --reverse
命令查看)。
圖 1-2 Go 語言開發(fā)日志
從早期提交日志中也可以看出,Go 語言是從 Ken Thompson 發(fā)明的 B 語言、Dennis M. Ritchie 發(fā)明的 C 語言逐步演化過來的,它首先是 C 語言家族的成員,因此很多人將 Go 語言稱為 21 世紀的 C 語言。
圖1-3是 Go 語言中來自貝爾實驗室特有并發(fā)編程基因的演化過程:
圖 1-3 Go 語言并發(fā)演化歷史
縱觀整個貝爾實驗室的編程語言的發(fā)展進程,從 B 語言、C 語言、Newsqueak、Alef、Limbo 語言一路走來,Go 語言繼承了來著貝爾實驗室的半個世紀的軟件設(shè)計基因,終于完成了 C 語言革新的使命??v觀這幾年來的發(fā)展趨勢,Go 語言已經(jīng)成為云計算、云存儲時代最重要的基礎(chǔ)編程語言。
按照慣例,介紹所有編程語言的第一個程序都是“Hello, World!”。雖然本教假設(shè)讀者已經(jīng)了解了 Go 語言,但是我們還是不想打破這個慣例(因為這個傳統(tǒng)正是從 Go 語言的前輩 C 語言傳承而來的)。下面的代碼展示的 Go 語言程序輸出的是中文“你好, 世界!”。
package main
import "fmt"
func main() {
fmt.Println("你好, 世界!")
}
將以上代碼保存到 hello.go
文件中。因為代碼中有非 ASCII 的中文字符,我們需要將文件的編碼顯式指定為無 BOM 的 UTF8 編碼格式(源文件采用 UTF8 編碼是 Go 語言規(guī)范所要求的)。然后進入命令行并切換到 hello.go
文件所在的目錄。目前我們可以將 Go 語言當作腳本語言,在命令行中直接輸入 go run hello.go
來運行程序。如果一切正常的話。應(yīng)該可以在命令行看到輸出 你好, 世界!
的結(jié)果。
現(xiàn)在,讓我們簡單介紹一下程序。所有的 Go 程序,都是由最基本的函數(shù)和變量構(gòu)成,函數(shù)和變量被組織到一個個單獨的 Go 源文件中,這些源文件再按照作者的意圖組織成合適的 package,最終這些 package 再有機地組成一個完整的 Go 語言程序。其中,函數(shù)用于包含一系列的語句(指明要執(zhí)行的操作序列),以及執(zhí)行操作時存放數(shù)據(jù)的變量。我們這個程序中函數(shù)的名字是 main。雖然Go語言中,函數(shù)的名字沒有太多的限制,但是 main 包中的 main 函數(shù)默認是每一個可執(zhí)行程序的入口。而 package
則用于包裝和組織相關(guān)的函數(shù)、變量和常量。在使用一個 package 之前,我們需要使用 import 語句導(dǎo)入包。例如,我們這個程序中導(dǎo)入了 fmt 包(fmt 是 format 單詞的縮寫,表示格式化相關(guān)的包),然后我們才可以使用 fmt 包中的 Println 函數(shù)。
而雙引號包含的“你好, 世界!”則是 Go 語言的字符串面值常量。和 C 語言中的字符串不同,Go 語言中的字符串內(nèi)容是不可變更的。在以字符串作為參數(shù)傳遞給 fmt.Println 函數(shù)時,字符串的內(nèi)容并沒有被復(fù)制——傳遞的僅僅是字符串的地址和長度(字符串的結(jié)構(gòu)在 reflect.StringHeader
中定義)。在 Go 語言中,函數(shù)參數(shù)都是以復(fù)制的方式(不支持以引用的方式)傳遞(比較特殊的是,Go 語言閉包函數(shù)對外部變量是以引用的方式使用)。
![]() | ![]() |
更多建議: