iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
自我挑戰組

跟著 Go 實戰聖經 一起自學 Go系列 第 13

DAY 13 Go 語言 的複合型別 - 陣列 (array)

  • 分享至 

  • xImage
  •  

昨天學習完 Go 語言中的核心型別,但若想要建立更複雜的資料,那就需要搭配複合型別,今天就先來介紹第一個複合型別- 陣列 (array)

今天我想要把水果分類成,喜歡的、不喜歡的兩種,然後把他們裝在兩個集合裡,在 Go 語言裡有所謂的 集合型別(collection types) ,內包含 陣列 (array)切片 (slice)映射表 (map) 來將多筆資料集中擺放,當然也可以方便我們走訪或是處理資料。

陣列 (array)

最標準的定義陣列時 一定 需要 陣列長度資料型別 ,資料型別可以是任意型別,但一次只能有一種型別。

[<長度>]<型別>

補充:
若定義時沒寫陣列長度一樣可以宣告成功,但他就不會是陣列, Go 語言會認為他是切片 (slice)。

當然我們也可以賦予陣列初始值,若沒有初始值則會是其型別的 零值 ,上面說定義陣列時必須要給予長度,但當有給予初始值時, Go 語言會根據初始值的數量來推測其陣列長度,但不是這樣就可以完全不寫喔!而是把陣列長度的地方換成[...],讓 Go 語言去推測。

[<長度>]<型別>{<初始值 1>,<初始值 2>,<初始值 3>,...<初始值 N>}

範例 1:

package main

import "fmt"

func array1() [5]int { // func 回傳一個長度為 5 型別為 int 的陣列
    var arr [5]int // 定義一個長度為 5 型別為 int 的陣列(沒有賦予初始值,會自動以零值為其初始值)
    return arr // 回傳 arr
}

func array2() [3]int { // func 回傳一個長度為 3 型別為 int 的陣列
    arr := [3]int{1,2,3} // 定義一個長度為 3 型別為 int 的陣列(有賦予初始值,會以賦予的值為其初始值)
    return arr // 回傳 arr
}

func array3() [4]int { // func 回傳一個長度為 4 型別為 int 的陣列
    arr := [4]int{9} // 定義一個長度為 4 型別為 int 的陣列(只有賦予一個初始值,其餘會以零值為初始值)
    return arr // 回傳 arr
}

func array4() [6]int { // func 回傳一個長度為 6 型別為 int 的陣列
    arr := [...]int{1,2,3,9,8,7} // 定義一個長度為 6 型別為 int 的陣列(有賦予初始值,將陣列長度省略為...)
    return arr // 回傳 arr
}

func main() {
    fmt.Println("array1:",array1())
    fmt.Println("array2:",array2())
    fmt.Println("array3:",array3())
    fmt.Println("array4:",array4())
}

範例 1(執行結果):

array1: [0 0 0 0 0]
array2: [1 2 3]
array3: [9 0 0 0]
array4: [1 2 3 9 8 7]

這時我持續開開心心的定義一個陣列如下:

範例 2:

package main

import "fmt"

func array5() [6]int { // func 回傳一個長度為 6 型別為 int 的陣列
     arr := [...]int{1,2,3} // 將陣列長度以...省略讓 Go 語言自己推測,且賦予 3 個初始值)
     return arr // 回傳 arr
 }

func main() {
    fmt.Println("array5:",array5())
}

範例 2(執行結果):

cannot use arr (variable of type [3]int) as type [6]int in return statement

結果直接噴錯了QQ,仔細思考或是看錯誤訊息其實不難發現,你只給予陣列三個初始值, Go 語言自己推測這是一個長度為 3 的陣列,但是在你又希望 func 回傳一個長度為 6 型別為 int 的陣列,這不是強人所難嗎?所以解決方法有二:

  1. 要不就是將func 回傳的陣列長度改為[3]int。
  2. 不然就是給予陣列 6 個初始值。

陣列比較

陣列與陣列同時也可以拿來比較,但前提是他們要 長度相同資料型別 ,就像你不會拿著五顆蘋果去問媽媽說這是兩顆蘋果嗎? (這個例子好無言,但是又好有畫面哈哈)
那接下來我們就來用各種方式來比對我們的陣列吧!

範例 3:

package main

import "fmt"

func compareArrays() (bool,bool,bool) { // func 回傳三個布林值
     arr1 := [...]int{1,2,3} 
     var arr2 [3]int
     arr3 := [...]int{0,0,0}
     arr4 := [3]int{1}
     return arr1 == arr2, arr2 == arr3, arr1 == arr4 // 回傳 arr 是否兩兩相等
 }

func main() {
    compare1,compare2,compare3 := compareArrays()
    fmt.Println("arr1 == arr2:",compare1)
    fmt.Println("arr2 == arr3:",compare2)
    fmt.Println("arr1 == arr4:",compare3)
}

範例 3(執行結果):

arr1 == arr2: false
arr2 == arr3: true
arr1 == arr4: false

這時我又開開心心,想來比較兩個陣列:

範例 4:

package main

import "fmt"

func compareArrays() (bool) { // func 回傳三個布林值
     arr3 := [...]int{0,0,0}
     var arr5 [6]int
     return arr3 == arr5 // 回傳 arr 是否兩兩相等
 }

func main() {
    compare4 := compareArrays()
    fmt.Println("arr3 == arr5:",compare4)
}

範例 4(執行結果):

invalid operation: arr3 == arr5 (mismatched types [3]int and [6]int)

啊!怎麼又錯了~錯誤訊息說 [3]int 和 [6]int 不能相比,原來我就是上面那個拿著不同顆蘋果問媽媽的人QQ
相信大家應該知道如何修改,這邊就交給大家自行修改了!

若想要在 Go 語言中比較集合,用 陣列 (array) 是非常方便的,而文章開頭說到的其他 集合型別(collection types) ,如 切片 (slice)映射表 (map) 是無法像陣列一樣這樣比較整個集合的,他們只能一一比對集合中的元素值。

陣列索引賦值

上面有學習到我們可以賦予陣列起始值,但有時候我只想要對陣列其中幾個索引值賦予初始值,其餘皆是零值,這也是可行的,且當你對索引賦予初始值時,不一定需要按照順序, Go 語言在這方面就有許多彈性。

[<長度>]<型別>{<索引 1>: <初始值 1>,<索引 2>:<初始值 2>,...<索引 n>:<初始值 n>}

範例 5:

package main

import "fmt"

var(
    arr1 [5]int
    arr2 = [...]int{4:0} // 若使用...讓 Go 推算陣列長度,即使索引 4 一樣是零值,還是要賦予初始值,讓 Go 有推算根據
    arr3 = [5]int{4:40, 2:20, 30} // 索引賦予初始值不按照順序也 ok ,且若是沒有給予索引,則會自動推算在前一個索引 +1
)

func main (){
    fmt.Println("arr1:",arr1)
    fmt.Println("arr2:",arr2)
    fmt.Println("arr3:",arr3)
    fmt.Println(arr1 == arr2) // 雖寫法不同,但實質上兩個陣列內容相同
}

範例 5(執行結果):

arr1: [0 0 0 0 0]
arr2: [0 0 0 0 0]
arr3: [0 0 20 30 40]
true

讀取陣列元素值

剛剛前面講了好多次的索引,索引到底是什麼?我通常把它想成在陣列裡的位置編號,陣列裡的第一個元素的位置編號永遠會從 0 起算,且每往下一個元素,其索引值都是加一,這使我們可以很方便地從陣列中取出元素值,如此一來若要拿陣列的最後一個元素也變得簡單許多,只需要把陣列長度-1,即可得出最後一個元素的值。

<值> = <陣列>[<索引>]

範例 6:

package main

import "fmt"

func murmur() string{
    arr := [...]string{
        "我最愛",
        "水果",
        "芒果",
        "是",
        "的",
    }
    return fmt.Sprintln(arr[0],arr[4],arr[1],arr[3],arr[2]) // fmt.Sprintln 將字串格式化
}

func main()  {
    fmt.Print(murmur()) // 利用索引值將陣列內的字串,拼成我的 murmur
}

範例 6(執行結果):

我最愛 的 水果 是 芒果

寫入值到陣列

陣列的值可以讀,就也可以寫入值到陣列中,有時候我們會先定義好一個陣列,然後當有資料的時候,再將值寫入陣列。

<陣列>[<索引>] = <值>

讓我們拿剛剛的範例六稍作修改...

範例 7:

package main

import "fmt"

func murmur() string{
    arr := [...]string{
        "我最愛",
        "水果",
        "芒果",
        "是",
        "的",
    }
    arr[2] = "榴槤" // 將索引 2 寫入新的值
    arr[0] = "我最討厭" // 將索引 0 寫入新的值
    return fmt.Sprintln(arr[0],arr[4],arr[1],arr[3],arr[2]) // fmt.Sprintln 將字串格式化
}

func main()  {
    fmt.Print(murmur())
}

範例 7(執行結果):

我最討厭 的 水果 是 榴槤

用迴圈走訪陣列

前面有說到陣列的索引值永遠都是從 0 起算,且每多一個元素,索引值都是 +1 ,因為其特性用迴圈來走訪陣列就是一個常用的做法,我們可以使用迴圈來將陣列中的每個元素做相同處理,如:對一個 int 型態的陣列每個元素 +5。

以下先來複習一下迴圈怎麼寫

for i := 0; i < len(<陣列>); i++ { //使用 len(<陣列>) 來計算陣列的長度
    // 迴圈內要對陣列做的事
}

範例 8:

package main

import "fmt"

func add5() string {
    addString := ""
    arr := [5]int{1,2,3,4,5}
    for i := 0; i < len(arr); i++ { //使用 len() 來計算陣列 arr 的長度
        arr[i] = arr[i] + 5
        addString += fmt.Sprintln("索引值",i,":","元素值",arr[i])
    }
    return addString
}

func main() {
    fmt.Println(add5())
}

範例 8(執行結果):

索引值 0 : 元素值 6
索引值 1 : 元素值 7
索引值 2 : 元素值 8
索引值 3 : 元素值 9
索引值 4 : 元素值 10

那今天陣列就介紹到這邊啦!不知道大家會不會跟我一樣覺得陣列有點麻煩,每次都一定需要陣列長度,但總會遇到我無法在一開始就確定陣列長度的狀況發生,所以 Go 語言就有一個切片(slice),他跟陣列非常相似,但又可以解決陣列的一些麻煩點,那我們就明天繼續來學習這個神奇的切片 (slice) 吧!


上一篇
DAY 12 Go 語言 的核心型別 - 布林值 (Boolean) 、字串 (String) 及 nil 值
下一篇
DAY 14 Go 語言 的複合型別 - 切片 (slice) 使用方式
系列文
跟著 Go 實戰聖經 一起自學 Go30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言