這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
心血來潮學了兩天Go,雖然有不少亮點,但感覺沒什麼吸引我的(既沒有像Ruby那樣簡潔、統一的文法,也沒有Lisp那樣強大的抽象機制)。
Go提供了一種叫goroutine的並發機制。“叫做goroutine是因為已有的短語——線程、協程、進程等等——傳遞了不準確的含義。goroutine 有簡單的模型:它是與其他goroutine 並存執行的,有著相同地址空間的函數。它是輕量的,僅比分配棧空間多一點點消耗。而初始時棧是很小的,所以它們也是廉價的,並且隨著需要在堆空間上分配(和釋放)。”[《學習Go語言》·第7章]
goroutine的文法很簡單,在一個函數前加上關鍵字go:
ready("Tee", 2) // 普通函數調用go ready("Tee", 2)// ready() 作為goroutine運行
下面這個例子來自Go語言入門教程——Eratosthenes素數篩法。
開始之前還得介紹一下Go語言中用於goroutine通訊的機制——channel。channel好比Unix下的雙向管道,其文法也很簡單:
ci := make(chan int)
這就建立了一個收發整數的channel,ci。
下面進入正題——用Go實現的Eratosthenes篩法:
如所示,把待篩選的數灌入某個過濾器Fn(它將過濾掉所有n的倍數),將結果作為下一個過濾器的輸入,重複這一過程。
generate()相當於初始化,從2開始一個個塞進channel。一輪篩選中,第一個從channel裡出來的數肯定是素數,以它為這一輪篩選的基底篩掉合數,把剩下的數塞進另一個channel(作為下一輪篩選的輸入)。
package mainimport fmt "fmt"// Send the sequence 2, 3, 4, ... to channel 'ch'.func generate(ch chan int) { for i := 2; ; i++ { ch <- i // Send 'i' to channel 'ch'. }}// Copy the values from channel 'in' to channel 'out',// removing those divisible by 'prime'.func filter(in, out chan int, prime int) { for { i := <-in // Receive value of new variable 'i' from 'in'. if i % prime != 0 { out <- i // Send 'i' to channel 'out'. } }}func main() { ch := make(chan int) // Create a new channel. go generate(ch) // Start generate() as a goroutine. for { prime := <-ch fmt.Println(prime) ch1 := make(chan int) go filter(ch, ch1, prime) ch = ch1 }}
你再也不要糾結於i++和++i了,Go只提供了後自增,即i++。
Go的文法與C的一個很大不同就是聲明順序是反的:先聲明變數名,再限定類型。其實,這倒不重要,Go較之C很重要的一點改進就是它可以進行類型推演,即根據所賦值的類型推定變數的類型。比如:
s := “hello, world”
根據所賦值為字串即可推定,變數s是string類型的。值得注意的一個地方是” := ”,其一般用於定義變數時順帶初始化(只能用在函數體內部)。
函數的聲明也比較特別,傳回型別是聲明在最後的(印象中,C的傳回型別是寫在最前面的吧):
func foo(input string) (output int) { return 4
}更奇特地是,在函數名前還有一個可選項,用來指定函數的接受者(下面的例子表明函數bla()必須由S類型的變數調用):package maintype S struct {i int}func (p S) bla() int {return p.i}func main() {v := S{4}println(v.bla())}
在物件導向編程裡估計很有用。
(未完待續)
P.S. 最後說說Go-lang和Plan 9的吉祥物,見圖