這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go 程式設計語言入門教程
原文:http://golang.org
翻譯:劉金雨/劉雲濤 <yuntao.liu#gmail.com> http://www.log4think.com
目錄
-
介紹
-
Hello, World
-
編譯
-
Echo
-
資料類型 Types
-
分配 Allocation
-
常量 Constants
-
I/O 包
-
Rotting cats
-
排序
-
列印輸出
-
素數
-
Multiplexing
介紹
本文檔是關於Go程式設計語言基礎的一個介紹性的入門教程,偏向於熟悉C或C++的讀者。本文並非一份語言的完整指南,如果需要的話,你應該去看看“語言規範”(language specification)。當讀完本教程之後,你可能會希望繼續看看“Effective Go”,這份文檔更深入的挖掘如何使用Go語言。此外還有一份《三日入門》的教程可供參考: 第一日,第二日, 第三日.
本文將會以一系列適當的程式來說明語言的一些關鍵特性。所有的樣本程式都是可啟動並執行(在撰寫本文時),並且這些程式都會提交到版本庫的/doc/progs/目錄下。
程式片段都會標註上在源檔案中的行號,為了清晰起見,空行前面的行號留空。
Hello, World
讓我們以最常見的方式開始吧:
05 package main
07 import fmt "fmt" // 本包實現了格式化輸入輸出
09 func main() {
10 fmt.Printf("Hello, world; or Καλημέρα κόσμε; or こんにちは 世界/n");
11 }
每份Go的源檔案都會使用 package 語句聲明它的包名。同時也可以通過匯入其它包來使用其中定義的功能。這段代碼匯入了包 fmt 來調用我們的老朋友——現在它的開頭字母是大寫的,並且前面帶有包名限定——fmt.Printf。
函數的聲明使用關鍵字func ,整個程式將會從為main 包中的main 函數開始(經過初始化之後)。
字串常量可以包含Unicode字元,採用UTF-8編碼。(事實上,所有Go程式的源檔案都是使用UTF-8編碼的)
注釋的方式同C++一樣:
/* ... */
// ...
稍後,我們會繼續談到print。
編譯
Go是一個編譯型語言。目前有兩個編譯器,其中gccgo編譯器採用了GCC作為後端,此外還有一系列根據其所適用的架構命名的編譯器,例如6g 適用於64位的x86結構,8g 適用於32位的x86結構,等等。這些編譯器比gccgo啟動並執行更快、產生的程式碼更加有效率。在撰寫本文的時候(2009年底),他們還具有一個更加健壯的運行期系統,但是gccgo 也正在迎頭趕上。
下面來看看如何編譯和運行程式。採用 6g 是這樣的
$ 6g helloworld.go # 編譯; 中間代碼位於 helloworld.6 中
$ 6l helloworld.6 # 連結; 輸出至 6.out
$ 6.out
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
$
gccgo 的方式看起來更加傳統一些。
$ gccgo helloworld.go
$ a.out
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
$
Echo
下一步,是來實現一個Unix的傳統命令Echo:
05 package main
07 import (
08 "os";
09 "flag"; // command line option parser
10 )
12 var omitNewline = flag.Bool("n", false, "don't print final newline")
14 const (
15 Space = " ";
16 Newline = "/n";
17 )
19 func main() {
20 flag.Parse(); // Scans the arg list and sets up flags
21 var s string = "";
22 for i := 0; i < flag.NArg(); i++ {
23 if i > 0 {
24 s += Space
25 }
26 s += flag.Arg(i);
27 }
28 if !*omitNewline {
29 s += Newline
30 }
31 os.Stdout.WriteString(s);
32 }
這段程式很小,但是卻有幾個特別之處。前面這個例子中,我們看到可以使用func 來聲明一個函數,同時關鍵字 var、const和type (目前還沒有用到)也可以用於聲明,就好像 import 一樣。注意,我們可以將同一類的聲明放到括弧中,以分號分隔,例如第7-10行和第14-17行。但也並非一定要如此,例如可以這樣寫
const Space = " "
const Newline = "/n"
分號在此處並不是必須的。事實上,任何頂層聲明後面都不需要分號。但如果要是在一個括弧內進行一系列的聲明,就需要用分號來分割了。
你可以像在C、C++或Java中那樣去使用分號,但如果你喜歡的話,在很多情況下都可以省略掉分號。分號是用於表示語句間的分隔,而非表示其中止。因此對於一個代碼塊中的最後一條語句來說,有無分號皆可。大括弧之後的分號也是可選的,就像C語言中的一樣。比對一下echo的原始碼,只有第8、15和21行必須要加分號,當然第22行中的 for 語句中為了分隔三個運算式也需要加分號。第9、16、26和31行的分號都不是必須的,加上分號只是為了以後再增加語句的時候方便而已。
這個程式匯入了os包以訪問 Stdout 變數,Stdout的類型是 *os.File 。import 語句實際上是個聲明:通常情況下(如hello world程式中那樣),它命名了一個標識符fmt用於訪問匯入的包的成員變數,而包是從目前的目錄或標準庫下的"fmt"檔案中匯入的。在這個程式中,我們為匯入的包顯式的指定了一個名字,預設情況下,包名是採用在匯入的包裡面已經定義好的名字,通常會與檔案名稱一致。因此在這個“hello world”程式中,可以唯寫 import "fmt" 。
你可以任意為包指定一個匯入名,但通常只有在解決名字衝突的情況下才有必要這樣做。
有了 os.Stdout,我們就可以用它的 WriteString 方法列印字串了。
匯入了 flag 包之後,第12行建立了一個全域變數來儲存 echo 的 -n 選項標誌。omitNewline 變數的類型是 *bool ——指向bool值的指標。
在 main.main中進行了參數解析,並建立了一個本地字串類型的變數用於構造輸出的內容。聲明語句如下
var s string = "";
這裡用到了關鍵字 var ,後面跟變數名和資料類型,之後可以繼續接=來賦初值。
Go 試圖盡量保持簡潔,這個聲明也可以用更短的形式。因為初值是一個字串類型的常量,沒有必要再聲明資料類型了, 因此這個聲明可以寫成這樣:
var s = "";
或者也可以直接用更短的形式:
s := "";
操作符 := 在 Go 語言裡經常會用在賦初值的聲明中,比如下面這個 for 語句的聲明:
22 for i := 0; i < flag.NArg(); i++ {
flag 包會解析命令列參數,並將參數值儲存在一個列表中。
Go語言中的 for 語句和C語言中的有幾個不同之處。首先,for是唯一的迴圈語句,沒有 while 語句或 do 語句。其次,for 語句後面的三個子句不需要圓括弧,但大括弧是必須的。這一條對 if 和 switch 語句同樣適用。稍後還會有幾個例子示範for 語句的其它用法。
迴圈體中通過追加(+=)標誌和空格構造字串 s 。迴圈之後,如果沒有設定 -n 標誌,程式追加一個空行,最後輸出結果。
注意,函數 main.main 沒有傳回值。它就是這樣定義的,如果到達了main.main 的末尾就表示“成功”,如果想表明出錯並返回,可以調用
os.Exit(1)
os 包還包含一些其他的常用功能,例如 os.Args 會被 flag 包用於訪問命令列參數。
(未完待續)