這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文轉載自http://www.cnblogs.com/AllenDang/archive/2012/03/03/2378534.html
我為什麼喜歡Go語言
從2000年至今,也寫了11年代碼了,期間用過VB、Delphi、C#、C++、Ruby、Python,一直在尋找一門符合自己心意和理念的語言。我很在意寫代碼時的手感和執行的效率,所以在Go出現之前一直沒有找到。在熟悉Go之後,我雖沒有停下腳步,也去體驗了D語言,但幾乎立即就放棄了,它的設計還是太複雜。
就說說Go吧。它的好其實也就兩個字——簡潔!
看很多朋友的留言都覺得這些“少個括弧、少個分號”之類的東西沒什麼意義,真的嗎?問題是,既然可以沒有,為什麼非得有?既然能夠少打一個字元,為什麼多打了還挺開心?還覺得天經地義?這裡簡單一點,那裡簡單一點,總的來說是不是就簡單了很多?這裡的設計簡潔一點,那裡簡潔一點,是否整體就是緊湊高效?
很多東西,要整體去體會,才能感覺到真正的強大。沒有前面這些文法上的各種“看起來沒什麼用”的支援,怎麼能做到後面提到的那些設計上的簡潔?
我堅信,少就是多,簡單就是強大,不能減一分的設計才是真正的好設計!
簡潔的變數聲明和賦值
拿最簡單的聲明變數和賦值來看,下面這一句完成了宣告類型到賦值,最後還有那個常見的分號作為語句的結束。
var i int = 10;
這個一點都不簡潔對吧?為什麼非要有“var”?為什麼不能自己推導變數類型?為什麼結尾非要加上分號?這三個問題,我相信Go語言的設計者也問過,並且都針對性的給了改進。重新來過。
i := 10
怎麼樣?“:=”是聲明並推導類型的文法糖,結尾的分號也省了,因為這裡我換行了,編譯器明白的。
還可以一次性聲明並賦值多個變數。
i, j, k := 1, 2, 3
不同的類型也可以。
i, j, k := 1, 1.0, “hello”
如果要聲明一堆變數,但暫時不賦值呢?可以這樣。
var (
i, j int
s string u, v, s = 2.0, 3.0, "bar"
)
Go的設計者甚至覺得多打幾個“var”都不應該!
簡潔的if
有點意思了對吧?我學習一門新語言的時候,第一眼看變數類型和聲明,第二眼就會去看邏輯控制的文法。現在來看看都有些什嗎?
if i > 10 {
println(“Greater then 10”)
}
稀鬆平常啊,難道一個簡單的if還能更簡單?恩,的確是的。首先if後面的條件判斷沒有人逼你再加上括弧了,僅僅是少了兩次按鍵嘛,還有呢?還有!下面這個應該是很常見的if使用情境。
result := SomeMethod()
if result > 0 {
}
很多時候result這個變數其實僅僅用於條件判斷,完全可以在if之後就扔掉,所以Go有了這麼個寫法。
if result := SomeMethod(); result > 0 {
}
這個運算式太常用了,真是誰寫誰知道,每次我寫著一行都會心裡一爽。來看看糾結一點的if段。
if a {
} else if b {
} else if c {
} else {
}
這種寫法是可以的,但不是Go推薦的,理由是可以更簡潔。比如強悍的switch。
強悍的switch
這是很大家熟知的switch用法,注意,沒有break哦!Go裡面case之間不會“下穿”。
switch tag { default:
s3() case 0, 1, 2, 3:
s1() case 4, 5, 6, 7:
s2()}
神奇一點的switch,嘿嘿,與if異曲同工之妙。
switch x := f(); { // missing switch expression means "true" case x < 0: return -x default: return x}
還有這個,有了這個更加明確的寫法,你真的還會if…else if…else if…else…嗎?
switch { case x < y: f1() case x < z: f2() case x == 4: f3()}
條件判斷舒服了,迴圈呢?
孤單的for
其實我一直不太明白,為什麼一門語言裡面要提供多個迴圈文法呢?for、while、do…while…都是不可替代的?用哪一個呢?似乎都是看個人愛好吧?可能大家隨便就可以舉個例子出來證明這三個東西存在的必要和細微的差別,但對於我來說,做同一件事情如果有多種方法其實就是設計上的冗餘,會對使用者造成或多或少的困擾。來看看Go的迴圈吧。
for i := 0; i < 10; i++ {
}
for a < b {
}
for {
}
看吧,一個for就搞定所有情況了。來看一個常用的遍曆集合,一把來說會寫成這樣。
count := len(someArray)
for i := 0; i < count; i++ {
println(someArray[i])
}
簡化這個,Go給出了一個關鍵字“range”,先看用法。
for i, value := range someArray {
// i 是整型,代表下標
// value就是數組內值的類型
}
range不單單可以用於數組,實際上它可以用於任何集合,比如map。
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}for i, s := range a { // type of i is int // type of s is string}
這裡只是提到了幾點最基本的文法情境,Go裡面還有很多!
函數可以返回多個值
其實能夠在一行多重賦值的語言挺多的,但一個函數能返回多個值的就很少了,比如在C#裡面如果要返回兩個int,通常會這麼幹。
public class TwoInts
{
public int A;
public int B;
}
public class Foo
{
public TwoInts ReturnTwoInt();
}
然後就可以 TwoInts ti = foo.CalcTwoInt() 覺得悲催嗎?也許你都麻木了對嗎?很多語言都是這麼設計的。函數只能返回一個值最大的問題是會導致出現很多沒必要的資料結構。上面就體現了這個冗餘,當然,你說可以用out關鍵字讓函數返回,但這個文法用起來就不是那麼安全了。而這個問題在Go裡面解決起來太容易了,因為Go的函數可以返回多個值!
func returnTwoInt() (int, int) {
}
a, b := returnTwoInt()
我對Go的好感就是從這裡萌芽的,這讓我的庫裡面從此少了很多資料結構!這無形中就能降低設計的複雜度。
函數內部聲明的對象指標可以安全的返回
func ReturnPointer() *Object1 {
obj := new Object1()
obj.A = “hello”
return obj
}
Go的記憶體回收行程會處理好這種情況的,放心啦!
異常處理?defer是啥?能吃嗎?
為什麼異常處理那麼複雜?多少人可以安全的實現下面這個邏輯?以下是虛擬碼。
File f = File.Read(“c:\\text.txt”)
f.Write(xxx)
f.Close()
我相信,有經驗的碼農們腦子裡面瞬間出現了各種版本的try…catch…finally…,還有各種各樣的書寫規範,比如“catch”裡面的邏輯不能在拋異常之類的東西。其實想想,我們的要求很簡單,開啟一個檔案,然後保證它在最後被關閉。僅此而已,為什麼做這麼簡單的一件事情非要那麼複雜?看看人家Go是怎麼做的!
func SaveSomething() {
if f, err := os.Open(“c:\\text.txt”); err == nil {
//各種讀寫
defer f.Close()
}
}
凡是加了defer的函數,都會在當前函數(這裡就是SaveSomething)執行完畢之後執行。就算“//各種讀寫”時發生異常f.Close也會堅定的在SaveSomething退出時被執行。有了這個,釋放點資源,關閉個把控制代碼這種小事再也無足掛齒!
介面再也不用“實現”了
從我接觸OO思想一來,凡是有介面的語言,都以不同的方式要求類“實現”介面,這樣的方式我一直都認為是天經地義的,直到我遇見了Go。
type Speaker interface {
Say()
}
上面定義了一個介面,只有一個方法,Say,不需要參數,也沒有傳回值。Go裡面,任何擁有某個介面所定義所有方法的東西,都預設實現了該介面。這是一句擁有太多內涵的話,足矣對設計思路產生重大的影響。比如下面這個方法,它接受一個類型為Speaker的參數。
func SaySomething(s Speaker) {
s.Say()
}
那麼所有擁有Say()方法的東西都可以往裡扔。
在Go的世界裡,所有的東西都預設實現了interface{}這個介面。有了這個概念,即使沒有泛型也能有效降低設計複雜度。
多線程還能更簡單點嗎?
要寫多線程,你要懂Thread,懂各種鎖,懂各種訊號量。在各類系統裡面,“非同步”邏輯通常代表“困難”。這是Go最強勁的部分,你見過比下面這個還簡單的非同步代碼嗎(以下代碼摘自Go的官方範例)?
func IsReady(what string, minutes int64) {
time.Sleep(minutes * 60*1e9);
fmt.Println(what, "is ready")
}
go IsReady("tea", 6);
go IsReady("coffee", 2);
fmt.Println("I'm waiting....");
執行的結果是,列印:
I'm waiting.... (right away)
coffee is ready (2 min later)
tea is ready (6 min later)
Go語言內建了“go”這個文法,任何go的方法,都將會被非同步執行。那非同步方法呼叫之前傳遞訊息呢?用channel唄。意如其名,就是一個管道,一個往裡寫,另外一個等著讀。
ch := make(chan int) //建立一個只能傳遞整型的管道
func pump(ch chan int) {
for i := 0; ; i++ { ch <- i } //往管道裡寫值
}
func suck(ch chan int) {
for { fmt.Println(<-ch) } //這裡會等著直到有值從管道裡面出來
}
go pump(ch) //非同步執行pump
go suck(ch) //非同步執行suck
嘿嘿,然後你就看到控制台上輸出了一堆數字。
這次就寫到這兒吧,對不住Go裡面其他的好東西了,哥餓了,就不一一出場亮相了,抱歉抱歉!鞠躬!下台!