1. 程式人生 > 實用技巧 >A tour of Go (3) - 更多型別:struct, slice和對映

A tour of Go (3) - 更多型別:struct, slice和對映

Saturday, December 19, 2020

A tour of Go (3) - 更多型別:struct, slice和對映

1. 指標

Go 擁有指標。指標儲存了值的記憶體地址

型別 *T 是指向 T 型別值的指標。其零值為 nil

var p *int // 定義一個指向 int 型別的指標

& 操作符會生成一個指向其運算元的指標

i := 42
p = &i	// 將變數地址賦值給指標變數

* 操作符表示指標指向的底層值

fmt.Println(*p) // 通過指標 p 讀取 i
*p = 21         // 通過指標 p 設定 i

與 C 不同,Go 沒有指標運算

2. 結構體

一個結構體(struct)就是一組欄位(field)。

type Vertex struct {
	X int
	Y int
}

結構體欄位:結構體欄位使用點號來訪問。

v := Vertex{1, 2}
v.X = 4

結構體指標:結構體欄位可以通過結構體指標來訪問。

可以通過 (*p).X 來訪問其欄位 X。允許我們使用隱式間接引用,直接寫 p.X 就可以。

p := &v // 不定義指標指向物件的屬性
p.X = 1e9

結構體文法:可以根據名字來賦值屬性,也可以只賦值部分,其他的根據型別自動賦值為預設值。

使用特殊字首 & 返回一個指向結構體的指標。

3. 陣列

型別 [n]T 表示擁有 nT 型別的值的陣列。

表示式:

var a [10]int // 宣告一個擁有10個整數的陣列

陣列的長度是其型別的一部分,因此陣列不能改變大小。


4. 切片

型別 []T 表示一個元素型別為 T 的切片。

a[low : high] // 通過冒號來分隔上界和下界

a[1:4] // 半開區間,排除最後一個元素

切片下界的預設值為 0,上界則是該切片的長度。

切片就像陣列的引用,切片本身並不儲存任何資料,更改切片的元素會修改其底層陣列中對應的元素。


切片文法類似於沒有長度的陣列文法。

[3]bool{true, true, false} // 陣列文法示例
[]bool{true, true, false} // 建立一個和上面相同的陣列,然後構建一個引用了它的切片

切片擁有 長度容量

切片 s 的長度和容量可通過表示式 len(s)cap(s) 來獲取。

指定下界會改變切片的容量,而上界則不會。

s := []int{2, 3, 5, 7, 11, 13} // len(s) = 6, cap = 6
s = s[:0] // len(s) = 0, cap = 6, 使用不同上界來引用,不會改變切片的容量!!
s = s[:4] // len(s) = 4, cap = 6, 增大上界,會拓展其長度,不改變容量!!
s = s[2:] // len(s) = 2, cap = 4, 指定下界,會捨棄前面n個值,改變切片的容量!!!
s = s[:5] // 報錯:slice bounds out of range [:5] with capacity 4

切片的零值是 nilnil 切片的長度和容量為 0 且沒有底層陣列。

表示式:var s[]int // 宣告一個 nil 切片


切片可以用內建函式 make 來建立,這也是你建立動態陣列的方式。

make 函式會分配一個元素為零值的陣列並返回一個引用了它的切片:

a := make([]int, 5)	   // len=5 cap=5 [0 0 0 0 0],省略了一個引數,預設len=cap=5
b := make([]int, 0, 5) // len=0 cap=5 []

切片的切片:切片可包含任何型別,甚至包括其它的切片。

board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
}
// 通過兩個索引值來改變指定的切片值
board[0][0] = "X"

為切片追加新的元素是種常用的操作,為此 Go 提供了內建的 append 函式。

var s []int // len=0 cap=0 []
s = append(s, 0) // len=1 cap=1 [0], append結果必須賦值給原來的切片
s = append(s, 1, 2, 3) // len=4 cap [0 1 2 3], 可以一次性新增多個元素

for 迴圈的 range 形式可遍歷切片或對映。

當使用 for 迴圈遍歷切片時,每次迭代都會返回兩個值。第一個值為當前元素的下標,第二個值為該下標所對應元素的一份副本

for i, value := range pow

for i := range pow // 只要索引,可以直接忽略第第二個變數
for i, _ := range pow // 用 _ 來忽略value
for _, value := range pow // 用 _ 來忽略i

練習:切片 https://tour.go-zh.org/moretypes/18

題目描述:實現 Pic。它應當返回一個長度為 dy 的切片,其中每個元素是一個長度為 dx,元素型別為 uint8 的切片。當你執行此程式時,它會將每個整數解釋為灰度值(好吧,其實是藍度值)並顯示它所對應的影象。

影象的選擇由你來定。幾個有趣的函式包括 (x+y)/2, x*y, x^y, x*log(y)x%(y+1)

(提示:需要使用迴圈來分配 [][]uint8 中的每個 []uint8;請使用 uint8(intValue) 在型別之間轉換;你可能會用到 math 包中的函式。)

package main

import (
	"golang.org/x/tour/pic"
	"math"
)

func Pic(dx, dy int) [][]uint8 {
	img := make([][]uint8, dy) // 初始化切片,這裡用 :=
	for i:=0; i<dy; i++ {
		img[i] = make([]uint8, dx)	// 這裡用 = ,不用 := 
		for j:=0; j<dx; j++ {
			img[i][j] = uint8(float64(i)*math.Log(float64(j))) //必須得型別轉換,再賦值
		}
	}
	return img
}

func main() {
	pic.Show(Pic)
}

i*j結果:

(i+j)/2結果:

i*log(j)結果:

i%(j+1)結果: