學習 Go 語言 1 — 基礎語法

1、第一個 Go 程序

最近在閒來無事之際開始學習點 Go 語言,Go 語言在近幾年很火,有 Docker 和 Kubernetes 這兩大殺器作支撐,它已是雲計算領域主流的編程語言了,而且有蠶食部分 C、C++、Java 等領域的趨勢,懷着一些對新技術的興趣,我開始學習了 Go 語言,在對其有了簡單的瞭解以後,我漸漸開始喜歡上這門語言了。python

目前我在工做中所使用的主要的編程語言是 Java,Java 雖然說優秀,可是我對其並無太大的好感,主要是它囉嗦複雜,當初學習它也主要是爲了找工做。嗯。。。有一絲絲慚愧。git

看到 Go 語言的 logo 我就以爲比較新奇,瞬間心情都變得不錯了(Go 語言的 logo 是一隻地鼠,英文稱 Gopher,如今不少 Go 開發者也稱本身爲 Gopher 了)。github

好了,閒話說了這麼多,開始學習吧。算法

在開始以前,你須要上 Go 官網下載 Go 語言所須要的開發工具包,而後安裝,安裝的步驟很是簡單,跟一個普通的應用程序沒有差異,這裏再也不贅述了。編程

而後再選擇一個本身喜歡的 IDE,我本身使用的是 Goland,固然 Vscode,Atom,Vim 也都有相應的支持 Go 語言的插件,選擇適合本身的便可。數組

仍是以一個經典的 Hello World 爲例子,開始第一個 Go 語言程序:數據結構

package main

import "fmt"

func main() {
   fmt.Println("Hello World!")
}

這段代碼雖然簡單,可是也包括了 Go 語言程序的基本結構:關鍵字 package 表明程序所在的模塊(包),import 引入代碼依賴,這兩個關鍵字的做用和 Java 中的十分相似。關鍵字 func 聲明一個函數,實現代碼邏輯。app

和其餘編程語言最主要的差異是,Go 語言的啓動入口 main 函數不支持任何返回值和參數,而且它必需要在名爲 main 的 package 下,不然 main 只是一個普通的函數。數據結構和算法

2、基本程序結構

2.1 變量與常量

Go 語言中的變量有下面幾種聲明的方式:編程語言

//1.
    var a int
    a = 1

    //2.
    var b string = "2"

    //3. 短變量聲明,自動類型推斷
    c := 10

    //4.
    var d = 123

    //5.
    var (
        x int = 1
        y string = "1"
    )

能夠看到,Go 語言的變量聲明可使用 var 關鍵字,後面跟變量名稱和類型,也能夠不設置變量的類型,Go 會自動推斷。

比較特殊的一點是 Go 能夠在一行代碼中對多個變量賦值(和 Python 同樣),這樣的話常見的交換兩個變量的值就能夠寫得很是簡潔了:

m, n := 10, 20
m, n = n, m   //交換 m n 的值

Go 語言中的常量主要經過關鍵字 const 來實現,聲明的方式通常有下面兩種:

const ABC = "TEST"
const(
   A = 1 
   B
   C = "123"
)

2.2 數據類型

Go 語言中的數據類型有下面表格中的這些:

Go 數據類型
布爾值 bool
字符串 string
整數 int int8 int16 int32 int64
無符號整數 uint uint8 uint16 uint32 uint64 uintptr
字節 byte //uint8 的別稱
Unicode 字符 rune // int32 的別稱
浮點數 float32 float64
複數 complex32 complex64

數據類型和主流的編程語言其實沒有太大的差異,只不過須要注意下面幾點:

  • Go 語言中不支持隱式的類型轉換,例以下面的代碼是不會經過編譯的:
var a int = 1
   var b int64
   b = a            //報錯
   
   b = int64(a)   //只能這樣轉換
  • 別名和原有類型也不能進行隱式的類型轉換
type MyInt int

func main() {
   var a int = 1
   var b MyInt
   b = a            //報錯
}
  • Go 語言中的 string 類型的默認值是空字符串而不是 nil
var s string
  fmt.Println("*" + s + "*") //初始化
  fmt.Println(len(s))
  fmt.Println(s == "")       //true

2.3 運算符

Go 中的運算符其實沒什麼可說的,若是你有其餘編程語言的經驗,那麼能夠徹底忽略掉這一部分可有可無的內容,由於它和其餘主流的編程語言沒有什麼差異。

第一類運算符是算術運算符,有下列幾種:

這裏須要注意一點的是,Go 語言中不支持前置的自增和自減運算符,因此 ++A--A 這種表達式是沒法經過編譯的。

第二類是比較運算符,這沒什麼可說的:

在 Go 語言中,若是是比較數組的話,那麼參與比較的數組的長度必須保持一致,不然沒法經過編譯:

x := [...]int{1, 2, 3}
y := [...]int{1, 2, 3}
//y := [...]int{1, 2, 3, 4}  沒法和 x 進行比較
fmt.Println(x == y)

第三類是邏輯運算符:

第四類是位運算符:

2.4 條件和循環

Go 語言中的條件語句仍然使用關鍵字 if 來實現,只不過 if 後面的條件表達式不會加上圓括號:

func oddOrEven(n int) {
   if n % 2 == 0{
      fmt.Printf("%d is even\n", n)
   }else {
      fmt.Printf("%d is odd\n", n)
   }
}

這裏須要注意的一點是 Go 語言中的 if 條件能夠支持變量賦值,以下:

if i := 1 == 1; i{
   fmt.Println("i == 1")
}else {
   fmt.Println("i != 1")
}

固然這段代碼僅僅是用來作演示,無任何實際的意義,if 條件中的賦值語句一般能夠用來對一個函數的調用的返回值進行判斷,下面是一個簡單的示例(Go 語言中的函數支持多返回值,這個後面在介紹函數時會說起):

func main() {
   if res, err := divide(10, 0); err != nil{
      fmt.Println("there is something wrong")
   }else {
      fmt.Println(res)
   }
}

func divide(x float32, y float32) (float32, error) {
   if y == 0{
      return 0, errors.New("cant`t divide by zero")
   }

   return x / y, nil
}

上面的代碼在 if 條件中調用了函數,函數若是 error 類型的返回值不爲空,則說明出現了錯誤,不然正常輸出結果。

還有一個常見的條件控制語句是 switch,Go 語言中也支持,基本的用法和其餘的編程語言沒有太大的差異:

for i := 0; i < 5; i++ {
   switch i {
   case 1, 3:
      fmt.Println("odd")
   case 2, 4:
      fmt.Println("even")
   default:
      fmt.Println("not between 0-3")
   }
}

能夠看到 case 條件後面能夠跟多個條件,而且每一個 case 塊中沒有帶 break 關鍵字,這是由於默認就有。

Go 語言中的循環只有關鍵字 for 來支持,而沒有常見的 while,for 關鍵字能夠代替 while 實現其功能,以下:

//常見的for使用
for i := 0; i < 10; i++ {
   fmt.Println(i * i)
}

//當作while使用,至關於 while(x > 0)
x := 10
for x > 0 {
   fmt.Println(x * x)
   x--
}

//無限循環,至關於while(true)
for {
   fmt.Println("I love Go")
}

3、經常使用數據結構

3.1 數組

先來看一下 Go 語言中的數組的幾種聲明方式:

var arr [3]int  //聲明數組並初始化爲零值
arr[0] = 10

arr2 := [3]int{1, 2}   //聲明時初始化
arr3 := make([]int, 2)    //聲明長度爲2的數組
arr4 := [3][3]int{{1, 2, 3}, {4, 5, 6}}    //多維數組的初始化

數組的常見操做是對其進行遍歷,咱們能夠按照一向的作法,寫一個 for 循環來完成,可是 Go 語言中的 range 關鍵字可使代碼更加的簡潔:

//常規的遍歷方式
for i := 0; i < len(arr2); i++ {
   fmt.Println(arr2[i])
}

//數組元素的遍歷
for i, v := range arr2 {
   fmt.Println(i, v)
}

//忽略下標或者值,_ 表示
for _, val := range arr2{
   fmt.Println(val)
}

數組還支持截取操做,這和 python 的特性很是相似:

//數組截取
fmt.Println(arr[0:2])
fmt.Println(arr[:2])
fmt.Println(arr[0:])
fmt.Println(arr[1:len(arr)])

3.2 切片

切片能夠看作是對數組的一層封裝,由於每一個切片的底層必定都是一個數組,先來看看切片的聲明方式:

var sli0 []int
sli0 = append(sli0, 1, 2)

sli1 := []int{}
sli1 = append(sli1, 1, 2, 3)

sli2 := []int{1, 2, 3}

sli3 := make([]int, 5) //指定長度,默認值0
fmt.Println(len(sli2))
fmt.Println(cap(sli2))

sli4 := make([]int, 5, 8)  //指定長度及容量,
fmt.Println(sli4[3])      //能夠訪問,默認值0
fmt.Println(sli4[7])      //沒法訪問

能夠看到切片的聲明方式其實和數組很是的相似,只是方括號中沒有了表示指定長度的數字。若是須要往切片中新增長元素,咱們可使用 append 函數:

sli := make([]int, 5)
sli = append(sli, 1)
sli = append(sli, 2, 3)

那麼數組和切片的區別都有哪些呢?

數組的長度必定是一個定值,就是其在聲明時所指定的長度,可是切片類型的長度是可變化的,切片的長度隨着其中元素的增加而增加,只不過卻不會隨着元素的減小而減小。

當切片的容量不夠時,那麼它會擴展至原來的兩倍,若是切片中的數據量大於等於 1024 的話,那麼每次擴容 1.25 倍。

在聲明切片時,能夠指定切片的長度及容量,若是不指定容量,那麼默認長度就是其容量。

//指定長度及容量
sli1 := make([]int, 5, 10)
fmt.Println(len(sli1))  //5
fmt.Println(cap(sli1))  //10

//不指定容量,那麼長度就是其容量
sli2 := make([]int, 5)
fmt.Println(len(sli2)) //5
fmt.Println(cap(sli2)) //5

你能夠將切片想象成一個數組上方的窗口,你只能經過這個窗口看見數組的一部分元素,這個窗口的大小就是切片的大小,而數組就是切片底層的數組。

例如上面聲明的切片 slie1,長度是 5,容量是 10,那麼切片可以看到的數組中的元素即是下標爲 0 - 4 的這五個元素。

3.3 集合

Go 語言中的 map 的聲明方式有下面的幾種:

map1 := map[string]int{}
map1["a"] = 1

map2 := map[string]int{
   "a": 1,
   "b": 2,
}

map3 := make(map[int]string, 10)

map 也可使用 range 關鍵字像數組那樣進行遍歷:

m := map[int]string{
   1: "a",
   2: "b",
   3: "c",
   4: "d",
}

for k, v := range m{
   fmt.Println(k, v)
}

須要注意的是,map 中若是一個鍵對應的值不存在,那麼它的默認值會是零值,例以下面的示例:

m := map[int]int{
   1: 1,
   2: 2,
   3: 3,
   4: 4,
}

fmt.Println(m[5])    //打印出 0

這樣的話就會存在一個問題,咱們怎麼判斷一個 map 中鍵對應的值,究竟是不存在仍是它的值原本就是 0 呢?其實訪問 map 中的值時,它會帶有兩個返回值,一個返回值是鍵對應的值,另外一個則是是否存在,能夠藉助這個返回值來判斷:

i, ok := m[5]
if ok{
   fmt.Println(i)
}else {
   fmt.Println("不存在鍵爲5")
}

瞭解了 map,你可能會很天然的想到 set,是的,set 也是一個很是重要而且經常使用的數據結構,可是在 Go 語言中並無 set 的實現,只不過咱們可使用 map 來實現一個 set,具體看代碼:

set := map[int]bool{}

//存放數據
set[1] = true
set[4] = true
set[4] = true

//刪除數據
delete(set, 1)

//判斷元素是否存在
if set[1] {
   fmt.Println("存在 1")
}else {
   fmt.Println("不存在 1")
}

//元素的個數
size := len(set)

上面的程序基本完成了常見的 set 的經常使用功能,其實現也很簡單,就是改造了一個 map,其鍵就至關於咱們 set 的值,而 map 鍵對應的值是一個 bool 值,若是爲 true 則表示存在於 set 中,若是爲 false 則表示不存在。

3.4 字符串

Go 語言中的字符串 string 是數據類型,而不是引用類型或者指針類型,這也是爲何前面提到的,string 的默認值是空字符串,而不是 nil。

字符串也能夠像數組同樣進行截取和遍歷操做:

s := "I like Golang"

//截取
fmt.Println(s[1])
fmt.Println(s[1:4])

//遍歷
for i, v := range s{
   fmt.Printf("%d, %c \n", i, v)
}

字符串的經常使用操做方法都在 strings 和 strconv 包下面,下面代碼給出了幾個示例:

s := "1,2,3"

//字符串分割
spliS := strings.Split(s, ",")
fmt.Println(spliS)

//是否包含
c := strings.Contains(s, "1")
fmt.Println(c)

//替換
reS := strings.Replace(s, ",", "-", -1)
fmt.Println(reS)

//strconv 包中的函數,主要是和其餘數據類型的轉換
v, _ := strconv.ParseBool("false")
fmt.Println(v)

Go 語言的基礎語法介紹的第一篇文章就結束了,若是你們有不懂的地方,或者對文中的內容有疑議,歡迎與我交流!

我一直認爲編程是一項熟能生巧的手藝,只有多寫代碼纔可以在實踐當中提高本身的編程能力。

爲此我在 Github 上新建了一個學習項目,使用 Java、Golang、Python 三種語言實現常見的數據結構和算法,以此作爲練習編程的素材,你能夠多看看代碼,而且本身多動手編寫,項目地址是 :

https://github.com/roseduan/algo-learn

相關文章
相關標籤/搜索