Golang入門——第一個程序Hello Golang

Hello Golang

package main
import "fmt"// 我們需要使用fmt包中的Println()函數
func main() {
fmt.Println("Hello, Golang. 你好,Golang! ")
}

1,Golang語法解讀

每個Go源代碼文件的開頭都是一個package聲明,表示該Go代碼所屬的包。包是Go語言裏最基本的分發單位,也是工程管理中依賴關係的體現。要生成Go可執行程序,必須建立一個名字爲main的包,並且在該包中包含一個叫main()的函數(該函數是Go可執行程序的執行起點)。Go語言的main()函數不能帶參數,也不能定義返回值。命令行傳入的參數在os.Args變量中保存。如果需要支持命令行開關,可使用flag包。在本書後面我們將解釋如何使用flag包來做命令行參數規範的定義,以及獲取和解析命令行參數。
在包聲明之後,是一系列的import語句,用於導入該程序所依賴的包。由於本示例程序用
到了Println()函數,所以需要導入該函數所屬的fmt包。這個包是對一些打印流的封裝
所有Go函數(包括在對象編程中會提到的類型成員函數)以關鍵字func開頭。一個常規的
函數定義包含以下部分:
func 函數名(參數列表)(返回值列表) {
// 函數體
}
對應的一個實例如下:
func Compute(value1 int, value2 float64)(result float64, err error) {
// 函數體
}
Go支持多個返回值。以上的示例函數Compute()返回了兩個值,一個叫result,另一個是
err。並不是所有返回值都必須賦值。在函數返回時沒有被明確賦值的返回值都會被設置爲默認
值,比如result會被設爲0.0, err會被設爲nil。err用來返回錯誤信息
Golang的註釋語法與java,C相似,
與C++保持一致,即同時支持以下兩種用法:
/*
塊註釋
*/
// 行註釋

**相信熟悉C和C++的讀者也發現了另外一點,即在這段Go示例代碼裏沒有出現分號。 Go
程序並不要求開發者在每個語句後面加上分號表示語句結束,這是與C和C++的一個明顯不同
之處。
有些讀者可能會自然地把左花括號{另起一行放置,這樣做的結果是Go編譯器報告編譯錯
誤,這點需要特別注意:
syntax error: unexpected semicolon or newline before {**

2,編譯環境

因爲大天朝的高牆,有些讀者可能沒有翻牆工具無法訪問 Golang官方下載地址:https://code.google.com/archive/p/go/downloads,所以博主給大家準備了一個翻牆神器,作爲做開發的翻牆工具 和VPN應該是必備的, lantern下載地址,以及下載之後的配置問題 博主就不在博文中說了,Golang安裝及配置 Golang 配置方法

3,編譯程序

假設之前介紹的Hello, world代碼被保存爲了hello.go,並位於~/goyard目錄下,那麼可以用以
下命令行編譯並直接運行該程序:
cd /goyard go run hello.go # 直接運行
Hello, world. 你好,世界!
使用這個命令,會將編譯、鏈接和運行3個步驟合併爲一步,運行完後在當前目錄下也看不
到任何中間文件和最終的可執行文件。如果要只生成編譯結果而不自動運行,我們也可以使用 Go
命令行工具的build命令:
cd /goyard go build hello.go
$ ./hello
Hello, world. 你好,世界!
可以看出, Go命令行工具是一個非常強大的源代碼管理工具。我們將在第4章中詳細講解Go
命令行工具所包含的更多更強大的功能。
從根本上說, Go命令行工具只是一個源代碼管理工具,或者說是一個前端。真正的Go編譯

器和鏈接器被Go命令行工具隱藏在後面,我們可以直接使用它們:
6ghelloworld.go 6l helloworld.6
$ ./6.out
Hello, world. 你好,世界!
6g和6l是64位版本的Go編譯器和鏈接器,對應的32位版本工具爲8g和8l。 Go還有另外一個
GCC版本的編譯器,名爲 gccg,這就不詳細說了

4, IDE的選擇

Google並沒有隨着Go 1的發佈推出官方的Go集成開發環境(IDE) ,因此開發者需要自行考
慮和選擇合適的開發工具。目前比較流行的開發工具如下:

  • 文本編輯工具gedit(Linux) /Notepad++(Windows) /Fraise(Mac OS X);
  • 安裝了GoClipse插件的Eclipse,集成性做得很好;
  • Vim/Emacs,萬能開發工具;
  • LiteIDE,一款專爲Go語言開發的集成開發環境。
    由於Go代碼的輕巧和模塊化特徵,其實一般的文本編輯工具就可以勝任Go開發工作。所以博主的用是Sublime3來編寫Golang代碼

5,工程管理

在實際的開發工作中,直接調用編譯器進行編譯和鏈接的場景是少而又少,因爲在工程中不
會簡單到只有一個源代碼文件,且源文件之間會有相互的依賴關係。如果這樣一個文件一個文件
逐步編譯,那不亞於一場災難。 Go語言的設計者作爲行業老將,自然不會忽略這一點。早期Go
語言使用makefile作爲臨時方案,到了Go 1發佈時引入了強大無比的Go命令行工具。
Go命令行工具的革命性之處在於徹底消除了工程文件的概念,完全用目錄結構和包名來推
導工程結構和構建順序。針對只有一個源文件的情況討論工程管理看起來會比較多餘,因爲這可
以直接用go run和go build搞定。下面我們將用一個更接近現實的虛擬項目來展示Go語言的
基本工程管理方法。
假設有這樣一個場景:我們需要開發一個基於命令行的計算器程序。下面爲此程序的基本

用法:
$ calc help
USAGE: calc command [arguments] ...
The commands are:
sqrt Square root of a non-negative value.
add Addition of two values.
$ calc sqrt 4 # 開根號
2
$ calc add 1 2 # 加法
3

我們假設這個工程被分割爲兩個部分:

  • 可執行程序,名爲calc,內部只包含一個calc.go文件;
  • 算法庫,名爲simplemath,每個command對應於一個同名的go文件,比如add.go。
    則一個正常的工程目錄組織應該如下所示:

    ├─
    ├─
    ├─calc.go
    ├─
    ├─add.go
    ├─add_test.go
    ├─sqrt.go
    ├─sqrt_test.go
    ├─
    ├─#包將被安裝到此處
    在上面的結構裏,帶尖括號的名字表示其爲目錄。 xxx_test.go表示的是一個對於xxx.go的單元
    測試,這也是Go工程裏的命名規則。
package main
import "os"// 用於獲得命令行參數os.Args
import "fmt"
import "simplemath"
import "strconv"
var Usage = func() {
fmt.Println("USAGE: calc command [arguments] ...")
fmt.Println("\nThe commands are:\n\tadd\tAddition of two values.\n\tsqrt\tSquare

root of a non-negative value.")
}
func main() {
args := os.Args
if args == nil || len(args) < 2 {
Usage()
return
}
switch args[0] {
case "add":
if len(args) != 3 {
fmt.Println("USAGE: calc add <integer1><integer2>")
return
}
v1, err1 := strconv.Atoi(args[1])
v2, err2 := strconv.Atoi(args[2])
if err1 != nil || err2 != nil {
fmt.Println("USAGE: calc add <integer1><integer2>")
return
}
ret := simplemath.Add(v1, v2)
fmt.Println("Result: ", ret)
case "sqrt":
if len(args) != 2 {
fmt.Println("USAGE: calc sqrt <integer>")
return
}
v, err := strconv.Atoi(args[1])
if err != nil {
fmt.Println("USAGE: calc sqrt <integer>")
return
}
ret := simplemath.Sqrt(v)
fmt.Println("Result: ", ret)
default:
Usage()
}
}

add.go

// add.go
package simplemath
func Add(a int, b int) int {
return a + b
}

add_test.go

// add_test.go
package simplemath
import "testing"
func TestAdd1(t *testing.T) {
r := Add(1, 2)
if r != 3 {
t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
}
}

sqrt.go

// sqrt.go
package simplemath
import "math"
func Sqrt(i int) int {
v := math.Sqrt(float64(i))
return int(v)
}

sqrt_test.go

// sqrt_test.go
package simplemath
import "testing"
func TestSqrt1(t *testing.T) {
v := Sqrt(16)
if v != 4 {
t.Errorf("Sqrt(16) failed. Got %v, expected 4.", v)
}
}

爲了能夠構建這個工程,需要先把這個工程的根目錄加入到環境變量GOPATH中。假設calcproj
目錄位於~/goyard下,則應編輯~/.bashrc文件,並添加下面這行代碼:
export GOPATH=~/goyard/calcproj
然後執行以下命令應用該設置:
$ source ~/.bashrc
GOPATH和PATH環境變量一樣,也可以接受多個路徑,並且路徑和路徑之間用冒號分割。

設置完GOPATH後,現在我們開始構建工程。假設我們希望把生成的可執行文件放到
calcproj/bin目錄中,需要執行的一系列指令如下:
cd /goyard/calcproj mkdir bin
cdbin go build calc
順利的話,將在該目錄下發現生成的一個叫做calc的可執行文件,執行該文件以查看幫助信
息並進行算術運算:
./calcUSAGE:calccommand[arguments]Thecommandsare:addAdditionoftwovalues.sqrtSquarerootofanonnegativevalue. ./calc add 2 3
Result: 5
$ ./calc sqrt 9
Result: 3
go build calc

這就是爲什麼說Go命令行工具是非常強大的。我們不需要寫makefile,因爲這個工具會替我
們分析,知道目標代碼的編譯結果應該是一個包還是一個可執行文件,並分析import語句以了
解包的依賴關係,從而在編譯calc.go之前先把依賴的simplemath編譯打包好。 Go命令行程序制
定的目錄結構規則讓代碼管理變得非常簡單。
另外,我們在寫simplemath包時,爲每一個關鍵的函數編寫了對應的單元測試代碼,分別
位於add_test.go和sqrt_test.go中。那麼我們到底怎麼運行這些單元測試呢?這也非常簡單。因爲
已經設置了GOPATH,所以可以在任意目錄下執行以下命令:
$ go test simplemath
ok simplemath0.014s

可以看到,運行結果列出了測試的內容、測試結果和測試時間。如果我故意把add_test.go的
代碼改成這樣的錯誤場景:
func TestAdd1(t *testing.T) {
r := Add(1, 2)
if r != 2 { // 這裏本該是3,故意改成2測試錯誤場景
t.Errorf(“Add(1, 2) failed. Got %d, expected 3.”, r)
}
}
然後我們再次執行單元測試,將得到如下的結果:
$ go test simplemath
— FAIL: TestAdd1 (0.00 seconds)
add_test.go:8: Add(1, 2) failed. Got 3, expected 3.
FAIL
FAILsimplemath0.013s
打印的錯誤信息非常簡潔,卻已經足夠讓開發者快速定位到問題代碼所在的文件和行數,從
而在最短的時間內確認是單元測試的問題還是程序的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章