變量、類型和關鍵字
完整的整數類型列表(符號和無符號)是
int8 , int16 , int32 , int64 和 byte , uint8 , uint16 , uint32 ,
uint64
混合用這些類型向變量賦值會引起編譯器錯誤,例如下面的代碼:
package main
func main() {
var a int //← 通用整數類型
var b int32 //← 32 位整數類型
a = 15
b = a + a //← 混合這些類型是非法的
//cannot use a + a (type int) as type int32 in assignment
println(b)
b = b + 5 //← 5 是一個(未定義類型的)常量,所以這沒問題
println(b)
}
字符串
字符串在 Go 中是 UTF-8 的由雙引號(”)包裹的字符序列。如果你使用單引號(’)則
表示一個字符(UTF-8 編碼)——這種在 Go 中 不是 string。
一旦給變量賦值,字符串就不能修改了:在 Go 中字符串是不可變的。從 C 來的用戶,
下面的情況在 Go 中是非法的。
var s string = "hello"
s[0] = 'c' //← 修改第一個字符爲 ’c’ ,這會報錯
在 Go 中實現這個,需要下面的方法:
package main
import "fmt"
func main() {
var s = "hello"
var c = [] rune (s)//轉換 s 爲 rune 數組
c[0] = 'c'//修改第一個字符爲 ’c’
var s2 = string (c)//創建 新的 字符串 s2 保存修改;
fmt.Printf("%s\n", s2)
}
多行字符串
基於分號的置入,你需要小心使用多行字符串。如果這樣寫:
s := "Starting part"
+ "Ending part"
會被轉換爲:
s := "Starting part" ;
+ "Ending part" ;
這是錯誤的語法,應當這樣寫:
//正確的方式一:
s := "Starting part" +
"Ending part"
正確的方式二:
Go 就不會在錯誤的地方插入分號。另一種方式是使用反引號 ` 作爲 原始 字符串符號:
//正確的方式二:
s := `Starting part
Ending part`
fmt.Printf("Value is: %v", s)//Printf() 的 %v 參數含義是用默格式打印這個值
控制結構
在 Go 中只有很少的幾個控制結構例如這裏沒有 do 或者 while 循環,只有
for 。有(靈活的) switch 語句和 if ,而 switch 接受像 for 那樣可選的初始化語句。還有叫做類型選擇和多路通訊轉接器的 select,語法有所不同(同 C 相比):
無需圓括號,而語句體必須 總是 包含在大括號內。
if
if x > 0 { //← { 是強制的
return y
} else {
return x
}
//===============
//下面的語法在 Go 中是非法的:
if err ! = nil
{ // ← 必須同 if 在同一行
return err
}
//if 和 switch 接受初始化語句,通常用於設置一個(局部)變量。
if err := Chmod(0664) ; err ! = nil { //← nil 與 C 的 NULL 類似
fmt.Printf(err) //← err 的作用域被限定在 if 內
return err
}
goto
Go 有 goto 語句——明智的使用它。用 goto 跳轉到一定是當前函數內定義的標籤。例如假設這樣一個循環:
package main
func main() {
myfunc()
}
func myfunc() {
i := 0
Here: // ← 這行的第一個詞,以分號結束作爲標籤,標籤大小寫敏感
println(i)
i++
if i > 10 { //打印1 到 10 ,這裏不加條件會是無限循環
return
}
goto Here// ← 跳轉
}
for循環
Go 的 for 循環有三種形式,只有其中的一種使用分號。
for init ; condition ; post { } //← 和 C 的 for 一樣
for condition { } //← 和 while 一樣
for { } //← 死循環
//由於 Go 沒有逗號表達式,而 ++ 和 – 是語句而不是表達式,如果你想在 for 中
//執行多個變量,應當使用 平行賦值。
// Reverse a
for i, j := 0, len (a)-1 ; i < j ; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i] //← 平行賦值
}
break 和 continue
循環嵌套循環時,可以在 break 後指定標籤。用標籤決定 哪個 循環被終止:
package main
func main() {
myfunc()
}
func myfunc() {
for j := 0 ; j < 5 ; j++ {
for i := 0 ; i < 10 ; i++ {
if i > 2 {
break //← 現在終止的是 j 循環,而不是 i 的那個
}
println(i)
}
println(j)
}
}
//continue
package main
func main() {
for i := 0 ; i < 10 ; i++ {
if i > 5 {
continue //← 跳過循環中所有的代碼 println(i)
}
println(i)
}
}
range迭代器
range 是個迭代器,當被調用的時候,從它循環的內容中返回一個鍵值對
package main
import "fmt"
func main() {
list := [] string { "a", "b", "c", "d", "e", "f" }
for k, v := range list {
// 對 k 和 v 做想做的事情
fmt.Printf("list[%d] = %s\n",k,v)
}
}
// list[0] = a
// list[1] = b
// list[2] = c
// list[3] = d
// list[4] = e
// list[5] = f
switch
可以使用 fallthrough 使其匹配失敗後自動向下嘗試。
沒有 fallthrough :
switch i {
case 0: // 空的 case 體
case 1:
f() // 當 i == 0 時, f 不會被調用!
}
而這樣:
switch i {
case 0: fallthrough
case 1:
f() // 當 i == 0 時, f 會被調用!
}
用 default 可以指定當其他所有分支都不匹配的時候的行爲。
switch i {
case 0:
case 1:
f()
default :
g() // 當 i 不等於 0 或 1 時調用
}
分支可以使用逗號分隔的列表。
package main
// import "fmt"
func main() {
println(shouldEscape('?')) //true
}
func shouldEscape(c byte ) bool {
switch c {
case ' ', '?', '&', '=', '#', '+':// ← , as ”or”
return true
}
return false
}
內建函數
//Go 中的預定義函數
close new panic complex
delete make recover real
len append print imag
cap copy println
可以使用命令查看函數文檔
go doc builtin // 獲得關於內建類型和函數的在線文檔。
查看某個函數文檔
//獲得copy的文檔 go doc builtin copy
close
//用於 channel 通訊。使用它來關閉 channel,
delete
//用於在 map 中刪除實例。
len 和 cap
//可用於不同的類型, len 用於返回字符串、slice 和數組的長度。
new
//用於各種類型的內存分配。
make
//用於內建類型(map、slice 和 channel)的內存分配。
copy
//用於複製 slice。
append
//用於追加 slice。
panic 和 recover
//用於 異常 處理機制。
print 和 println
//是底層打印函數,可以在不引入 fmt 包的情況下使用。它們主要用於調試。
complex 、 real 和 imag
//全部用於處理 複數。
array、slices 和 map
array
array 由 [n]<type>
定義,n 標示 array 的長度,而 <type>
標示希望存儲的內容的類型。對 array 的元素賦值或索引是由方括號完成的:
package main
import "fmt"
func main() {
// var arr = [10] int 這樣的數組類型有固定的大小。大小是類型的一部分 。由於不同的大小是不同的類型,因此不能改變大小
var arr [10] int
arr[0] = 42
arr[1] = 13
fmt.Printf("The first element is %d\n", arr[0])
}
var arr = [10] int
這樣的數組類型有固定的大小。大小是類型的一部分 。由於不同的大小是不同的類型,因此不能改變大小。數組同樣是值類型的:將一個數組賦值給另一個數組,會 複製 所有的元素。尤其是當向函數內傳遞一個數組的時候,它會獲得一個數組的副本,而不是數組的指針。
可以像這樣聲明一個數組:var a [3] int,如果不使用零來初始化它,則用複合聲明:
a := [3] int{ 1, 2, 3 }
也可以簡寫爲 a := [...] int{ 1, 2, 3 }
,Go 會自動統計元素的個數。
//多維數組
a := [3][2] int { [2] int { 1,2 } , [2] int { 3,4 } , [2] int { 5,6 } }
slice
slice 是一個 指向 array 的指針,這是其與 array 不同的地方;slice 是引用
類型,這意味着當賦值某個 slice 到另外一個變量,兩個引用會指向同一個 array。
sl := make ([] int , 10)
//錯誤的示例,如果要擴展slice需要使用append和copy
package main
func main() {
var array [100] int //← Create array, index from 0 to 99
slice := array[0:99] //← Create slice, index from 0 to 98
slice[98] = 'a'// ← OK
slice[99] = 'a'// ← Error: ”throw: index out of range”
}
使用append擴展slice
package main
import "fmt"
func main() {
s0 := [] int { 0, 0 }
s1 := append (s0, 2)//追加一個元素, s1 == []int{0, 0, 2} ;
fmt.Printf("%v\n",s1)
s2 := append (s1, 3, 5, 7)//追加多個元素, s2 == []int{0, 0, 2, 3, 5, 7} ;
fmt.Printf("%v\n",s2)
//...可變長參數 http://studygolang.com/articles/1965
s3 := append (s2, s0...)//追加一個 slice, s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} 。注意這三個點!
fmt.Printf("%v\n",s3)
}
// [0 0 2]
// [0 0 2 3 5 7]
// [0 0 2 3 5 7 0 0]
copy的使用
package main
import "fmt"
func main() {
var a = [...] int { 0, 1, 2, 3, 4, 5, 6, 7 }
var s = make ([] int , 6)
n1 := copy (s, a[0:])// ← n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
fmt.Printf("%v\n",s)
fmt.Printf("%v\n",n1)
//copy返回拷貝的元素個數
n2 := copy (s, s[2:])// ← n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
fmt.Printf("%v\n",s)
fmt.Printf("%v\n",n2)
}
map
一般定義 map 的方法是: map[<from type>]<to type>
package main
import "fmt"
func main() {
monthdays := map [ string ] int {
"Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31,// ← 逗號是必須的
}
// monthdays := make ( map[ string ] int )//當只需要聲明一個 map 的時候,使用 make 的形式
fmt.Printf("%d\n", monthdays["Dec"])//打印出 12 月的天數
year := 0
for _, days := range monthdays { //← 鍵沒有使用,因此用 _, days
year += days
}
fmt.Printf("Numbers of days in a year: %d\n", year)
//向 map 增加元素,可以這樣做:
monthdays["Undecim"] = 30 //← 添加一個月
monthdays["Feb"] = 29 //← 閏年時重寫這個元素
//檢查元素是否存在的兩種方式
//var value int
var present bool
//value, present = monthdays["Jan"]
_, present = monthdays["Jan"] //← 如果存在, present 則有值 true
println(present)
//← 或者更接近 Go 的方式
//v, ok := monthdays["Jan"]
_, ok := monthdays["Jan"]// ← “逗號 ok ”形式
println(ok)
//也可以從 map 中移除元素:
delete(monthdays, "Mar") //← 刪除 ”Mar” 吧
}
參考文章: