owned this note
owned this note
Published
Linked with GitHub
###### tags: `GoLang`
# GoLang 02.進階語法:
### Go 陣列(Array)宣告
> 一維陣列的宣告方式如下:
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">陣列名稱</span> = [<span style="color: lightgreen">陣列長度</span>]<span style="color: lightgreen">元素型別</span>{<span style="color: yellow">元素初始值1</span>,<span style="color: yellow">元素初始值2</span>}** \
> > *陣列長度可以使用 ```[...]``` 讓編譯器自動計算長度\
> > *元素型別可為任何資料型別的\
> > *元素初始值若不賦予則為元素型別的預設值\
> > *元素初始值可以依序使用 ```,``` 分隔並可以向右增加元素,或是直接指定元素位置賦值
> > 例如 : **<span style="color: LemonChiffon">ar := [...]int{</span><span style="color: Orange">0</span><span style="color: LemonChiffon">:99}</span>** 其中<span style="color: Orange">0</span>表示為第一個元素
>
>二維陣列的宣告方式如下:
>``` go
> package main
> import "fmt"
> func main() {
> vArr1 := [3][3]int{
> {1, 2, 3},
> {4, 5, 6},
> {7, 8, 9}}
> fmt.Println(vArr1)
> }
>```
>陣列本身為值類型變數其記憶體位址與元素[0]為位址相同,陣列的宣告與記憶體運作:
>``` go
>package main
>import "fmt"
>func main() {
>
> var vArray = [...]int{1, 2, 3}
> fmt.Printf("陣列位址 : %p\n", &vArray)
>
> fmt.Printf("元素[0] 位址 : %p 數值 : %v\n", &vArray[0], vArray[0])
> fmt.Printf("元素[1] 位址 : %p 數值 : %v\n", &vArray[1], vArray[1])
> fmt.Printf("元素[2] 位址 : %p 數值 : %v\n", &vArray[2], vArray[2])
>
>}
>
>```
><img src="http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuSf9JIjHACbNACfCpoXHICaiIaqkoSpFu-8gI4pEJanFLL1wENltYzPbIWhAAM1GpSN5cLQKc9kgy6B5Z6TjsrwkdbBDnGaLUhfkyTE9NIzdBdxQCJqphyJOwEcyResdSpOydTjWPL1XO92Oac9We0bIpvcNdvAgu5XKb5ZfcLSI3HBXrDK5KpP9Ti-thtWnSxvLj46iWiBJXZrMBnTCUB5YBP2bW6uO3es661eLqBLQuD1gH52h8Ogjx_Vqeg773GrFTYzzjdR9ouNpdlMj078nrQ2PG0O2QOtbuihBBqc5nbv-6Zn05PJpYnBpqbEvdaxeWKJ7qzRbpsUrF6rjVhvbmTEzUu2kUxAp--MqoH2lSJsTlUlfkbdFP-zuEMlAanqDJnkwmV47qjAxuFcK5SzNxW8gvahDIr40sGYql9OBY6gv7812m8G40000" style="background-color:#2e2e2e;padding:0px;">
>
>
> 1. GoLang陣列是屬於值類型其長度固定無法改變,不同長度的陣列會被視為不同資料型別
> 2. GoLang陣列是屬於值類型其值的傳遞,即表示將其陣列複製到另一新的陣列中
> 3. GoLang陣列宣告利用鍵值(ASCII)或正整數當作元素編號並且直接賦值,其餘未賦值則會保持預設值
>``` go
>package main
>import "fmt"
>func main() {
> vArray := [...]int{1, 2, 3}
> fmt.Println("原始 :", vArray)
> fcArrayChange(vArray)
> fmt.Println("傳遞後 :", vArray)
>
> // 原始 : [1 2 3]
> // 函式內 : [0 0 0]
> // 傳遞後 : [1 2 3]
>
> vArrayKey := [...]int{3: 555}
> fmt.Println("\n鍵值 :", vArrayKey)
> //[0 0 0 555]
> //陣列可利用鍵值或數字當作元素編號並且直接賦值,其餘未賦值則會保持預設值
>
> var vArrayNew [3]int = [...]int{4, 5, 6}
> fmt.Println("\n複製 :", vArrayNew)
> // 最複雜宣告方式
>
> var vArrayCopy [3]int = vArray
> fmt.Println("\n複製 :", vArrayCopy)
> // 陣列複製
>
> if vArray == vArrayCopy {
> fmt.Println("\nArray和vArrayCopy兩者相等")
> }
>
>}
>
>func fcArrayChange(inputArr [3]int) {
> inputArr[0] = 0
> inputArr[1] = 0
> inputArr[2] = 0
> fmt.Println("函式內 :", inputArr)
> //傳遞函式內之陣列即為新的陣列
>}
>```
### Go 陣列(Array)使用範例(for-range)
``` go
package main
import "fmt"
func main() {
vArr1 := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}}
fmt.Printf("vArr1[0] (%p)\n", &vArr1[0])
fmt.Printf("vArr1[1] (%p)\n", &vArr1[1])
fmt.Printf("vArr1[3] (%p)\n", &vArr1[2])
//使用for迴圈解析二維以上陣列需要使用兩層以上迴圈
for index1 := 0; index1 < len(vArr1); index1++ {
for index2 := 0; index2 < len(vArr1[index1]); index2++ {
fmt.Printf("[%v][%v]= %v (%p)\n", index1, index2, vArr1[index1][index2], &vArr1[index1][index2])
}
}
fmt.Println("\n")
// for-range 使用於arrary範例1
for index, value := range vArr1 {
fmt.Printf(" index = %v , value = %v ;", index, value)
} // index = 0 , value = [1 2 3] ; index = 1 , value = [4 5 6] ; index = 2 , value = [7 8 9] ;
fmt.Println("\n")
// for-range 使用於arrary範例2
for index1, value := range vArr1 {
for index2, value := range value {
fmt.Printf("vArr1[%v][%v] = %v\n", index1, index2, value)
}
}
// vArr1[0][0] = 1
// vArr1[0][1] = 2
// vArr1[0][2] = 3
// vArr1[1][0] = 4
// vArr1[1][1] = 5
// vArr1[1][2] = 6
// vArr1[2][0] = 7
// vArr1[2][1] = 8
// vArr1[2][2] = 9
}
```
### Go 陣列(Array)應用範例:
>
> ``` go
> package main
> import "fmt"
> func main() {
>
> vArray := [...]int{1, 99, 3}
> fmt.Println("原始 :", vArray)
>
> // 求陣列內所有元素最大值
> vMaxNum := vArray[0]
> for i := 0; i < len(vArray); i++ {
> if vMaxNum < vArray[i] {
> vMaxNum = vArray[i]
> }
>
> }
> fmt.Println("最大值 :", vMaxNum)
>
> // 求陣列內所有元素平均值
> var vSum int
> for _, value := range vArray {
> vSum += value
>
> }
> fmt.Printf("平均值 : %v", float64(vSum)/float64(len(vArray)))
> }
> ```
>
> ``` go
> package main
> import (
> "fmt"
> "math/rand"
> "time"
> )
>
> func main() {
>
> // 生成一隨機元素(值0~99)之陣列
> var vArray [5]int
> rand.Seed(time.Now().UnixNano())
> for i := 0; i < len(vArray); i++ {
> vArray[i] = rand.Intn(100)
>
> }
> fmt.Println("原始 :", vArray)
>
> // 將陣列的元素位置倒轉
> var vArrayLen = len(vArray)
> for i := 0; i < (vArrayLen / 2); i++ {
> vArray[i], vArray[vArrayLen-1-i] = vArray[vArrayLen-1-i], vArray[i]
> }
>
> fmt.Println("倒轉 :", vArray)
>
> }
>
> ```
---
### Go 切片(slice)宣告
> 切片(slice)可視作可變動長度的陣列,但其本身為引用型別
>
> 切片的第一種宣告方式(直接創建無名稱陣列):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">切片名稱</span> = []<span style="color: lightgreen">元素型別</span>{<span style="color: yellow">元素初始值1</span>,<span style="color: yellow">元素初始值2</span>}** \
> > *元素型別可為資料型別的 ```邏輯值、數值、字串```???引用類型?\
> > *元素初始值若不賦予則為元素型別的預設值\
> > *元素初始值可以依序使用 ```,``` 分隔並可以向右增加元素
>
> 切片的第二種宣告方式(引入已被創建的陣列):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">切片名稱</span> = 陣列名稱[<span style="color: yellow">開始元素位置</span>:<span style="color: Orange">結束元素位置</span>]** \
> > *"開始元素位置"即選擇被引入陣列的元素位置作為引入起始\
> > *"結束元素位置"即選擇被引入陣列的"元素位置"(第幾個元素)作為引入結束\
> > *元素位置可以使用 ``` ``` (空白)作為"第一個元素位置"或"最後一個元素位置"作為簡寫
>
> 切片的第三種宣告方式(使用make函式創建):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">切片名稱</span> = make([]<span style="color: lightgreen">元素型別</span>,<span style="color: yellow">切片長度</span>,<span style="color: yellow">切片容量</span>)** \
> > *切片長度<=切片容量,切片容量可藉由指令變動大小(記憶體位址也會改變)\
> > *使用make函式所創建的切片,相當於直接創建無名稱且0值陣列做為切片
>
> 切片的字串宣告方式(限用於宣告字串):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">切片名稱</span> = []<span style="color: lightgreen">字串型別</span>(<span style="color: yellow">字串</span>)** \
> > *字串型別為 ```byte```、```int32```、```rune```
>
>切片的宣告方式如下:
>```go
>package main
>import "fmt"
>func main() {
>
> //切片的第一種宣告方式(直接創建無名稱陣列):
> var vSlice1 = []int{0, 1, 2}
> fmt.Printf("vSlice1切片值 : %v\n", vSlice1)
> fmt.Printf("vSlice1的長度 : %v\n", len(vSlice1))
> fmt.Printf("vSlice1的容量 : %v\n", cap(vSlice1))
> fmt.Printf("vSlice1的位址 : %p\n", &vSlice1)
> fmt.Printf("vSlice1[0] 位址=%p 值=%v\n", &vSlice1[0], vSlice1[0])
> fmt.Printf("vSlice1[1] 位址=%p 值=%v\n", &vSlice1[1], vSlice1[1])
> fmt.Printf("vSlice1[2] 位址=%p 值=%v\n", &vSlice1[2], vSlice1[2])
>
> fmt.Printf("\n")
> //切片的第二種宣告方式(引入已被創建的陣列):
> var vArray2 = [...]int{0, 1, 2}
> var vSlice2 = vArray2[0:3]
> fmt.Printf("vSlice2切片值 : %v\n", vSlice2)
> fmt.Printf("vSlice2的長度 : %v\n", len(vSlice2))
> fmt.Printf("vSlice2的容量 : %v\n", cap(vSlice2))
> fmt.Printf("vSlice2的位址 : %p\n", &vSlice2)
> fmt.Printf("vSlice2[0] 位址=%p 值=%v\n", &vSlice2[0], vSlice2[0])
> fmt.Printf("vSlice2[1] 位址=%p 值=%v\n", &vSlice2[1], vSlice2[1])
> fmt.Printf("vSlice2[2] 位址=%p 值=%v\n", &vSlice2[2], vSlice2[2])
>
> vSlice2[0] = 4
> vSlice2[1] = 5
> vSlice2[2] = 6
> fmt.Printf("對元素賦值\n")
> fmt.Printf("vSlice2切片值 : %v\n", vSlice2)
> fmt.Printf("vArray2陣列值 : %v\n", vArray2)
>
> fmt.Printf("\n")
> //切片的第三種宣告方式(使用make函式創建):
> var vSlice3 = make([]int, 3, 3)
> vSlice3[0] = 0
> vSlice3[1] = 1
> vSlice3[2] = 2
> fmt.Printf("vSlice3切片值 : %v\n", vSlice3)
> fmt.Printf("vSlice3的長度 : %v\n", len(vSlice3))
> fmt.Printf("vSlice3的容量 : %v\n", cap(vSlice3))
> fmt.Printf("vSlice3的位址 : %p\n", &vSlice3)
> fmt.Printf("vSlice3[0] 位址=%p 值=%v\n", &vSlice3[0], vSlice3[0])
> fmt.Printf("vSlice3[1] 位址=%p 值=%v\n", &vSlice3[1], vSlice3[1])
> fmt.Printf("vSlice3[2] 位址=%p 值=%v\n", &vSlice3[2], vSlice3[2])
>}
>
>```
>
><img src="http://www.plantuml.com/plantuml/svg/bLBDhj9055vtdk84Dc0iARZHfY6uz1a8Yx4TeA5CoJ0c6cBIXR4e_c02KOoW1j4c1k96YShRwBHqvIiuhPZSsnjoxvsSpC_vpdVcp9clrkUOyJjT1pcaoO6xmEnMcuDbCs9oswK8eHusExX58BSFNeKFlyMV9pd8M-aO84LfCjmb83PByU9nF5kAuKkRyciLiHWLn9ilOkxfsbrDmvQbQSKaNg6kHU0cO_ZUBShhYKTHrNGnRbUZRR2VlTNhMWD0U3iTudVZVR2MMm38vZl2BL-0U_q8jt8-jtpgtZ-xfndxvNc_Vwx2zOVu_I9y5iZZsPXiXijRGct8wJwIXlwt4mnL1J7qeqT-zFf1z7KSVXeXP3gutuUi7mm32hMhBZFRjbK4E3plXbQjmW0bJGcV-B4_1nskr4kbKaF-AqgBbb0ytOeV7oNa40ed0R7v7llF9M3YNWeWMHvrEG6NWcFJ3kHTXccBw0VD_Akg87RJQ198rOZXgeXgX5fIbNy1" style="background-color:#2e2e2e;padding:0px;">
>
### Go 切片append()& copy 使用範例
>切片可藉由append()函式進行擴容,擴容後其切片長度與容量都會增加,
>append()函式僅能拼接相同型別元素或切片於新的切片當中,
>其擴容後將新切片之元素分配新的記憶體位址(在原切片引入長度超過陣列長度下),
>如果新切片名稱與原切片名稱相同,則切片位址將不會改變。
>
> > append()函式擴容方式(拼接新的元素):
> >
> > **<span style="color: DodgerBlue">擴容後切片名稱</span> = append(<span style="color: lightgreen">被擴容切片名稱</span>,<span style="color: yellow">元素初始值1</span>,<span style="color: yellow">元素初始值2</span>}**
> >
> > *元素型別必須與擴容後切片型別相同\
> > *元素初始值若可以依序使用 ```,``` 分隔並可以向右增加元素
>
> > append()函式擴容方式(拼接原有的切片):
> >
> > **<span style="color: DodgerBlue">擴容後切片名稱</span> = append(<span style="color: lightgreen">被擴容切片名稱</span>,<span style="color: yellow">拼接切片名稱1</span>,<span style="color: yellow">拼接切片名稱2</span><span style="color: red">...</span>}**
> >
> > *切片型別必須與擴容後切片型別相同\
> > *拼接切片可以依序使用 ```,``` 分隔並可以向右增加切片\
> > *拼接切片時append()指令中 ```...``` 不可省略
>
>
>切片可藉由copy()函式將切片中的元素值寫入另一切片中,
>其過程依序將元素寫入直至切片長度結束為止,
>完成後其切片的長度將不會減少或是增加,前後記憶體位址都將保持不變
> > copy()用法如下:
> >
> > **copy(<span style="color: lightgreen">被寫入切片名稱</span>,<span style="color: yellow">寫入切片名稱</span>}**
> >
> > *切片型別必須與copy後切片型別相同
>
>``` go
>package main
>import "fmt"
>func main() {
> var vArray1 = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
> var vSlice1 = vArray1[1:8]
>
> fmt.Printf("vArray1陣列值 : %v\n", vArray1) //[0 1 2 3 4 5 6 7 8 9]
> fmt.Printf("vSlice1切片值 : %v\n", vSlice1) //[1 2 3 4 5 6 7]
> fmt.Printf("vSlice1的長度 : %v\n", len(vSlice1)) //7
> fmt.Printf("vSlice1的容量 : %v\n", cap(vSlice1)) //9
> fmt.Printf("vSlice1的位址 : %p\n", &vSlice1)
> fmt.Println()
>
> //vSlice1引入vArray1 其元素是直接指向的(對陣列進行切片)
> for i := 0; i < len(vArray1); i++ {
> fmt.Printf("vArray1[%v] 值:%v 位址:%p\n", i, vArray1[i], &vArray1[i])
> }
>
> fmt.Println()
> //使用for迴圈列出所有元素值
> for i := 0; i < len(vSlice1); i++ {
> fmt.Printf("vSlice1[%v] 值:%v 位址:%p\n", i, vSlice1[i], &vSlice1[i])
> }
>
> fmt.Println()
> //使用for-range迴圈列出所有元素值
> for index, value := range vSlice1 {
> fmt.Printf("vSlice1[%v] 值:%v 位址:%p\n", index, value, &vSlice1[index])
> }
>
> fmt.Println()
>
> //利用append函式直接擴充切片元素成為一個新切片
> vSlice2 := append(vSlice1, 10, 11, 12, 13)
> fmt.Printf("vSlice2切片值 : %v\n", vSlice2) //[1 2 3 4 5 6 7 10 11 12 13]
> fmt.Printf("vSlice2的長度 : %v\n", len(vSlice2)) //11
> fmt.Printf("vSlice2的容量 : %v\n", cap(vSlice2)) //18
>
> fmt.Println()
> //利用append函式拼接兩個切片成為一個新切片
> vSlice3 := append(vSlice1, vSlice2...)
>
> fmt.Printf("vSlice3切片值 : %v (將Slice1與Slice2拼接成Slice3)\n", vSlice3) // [1 2 3 4 5 6 7 1 2 3 4 5 6 7 10 11 12 13]
> fmt.Printf("vSlice3的長度 : %v\n", len(vSlice3)) //18
> fmt.Printf("vSlice3的容量 : %v\n", cap(vSlice3)) //18
>
> fmt.Println()
> fmt.Printf("vSlice2的位址 : %p\n", &vSlice2)
> for index, value := range vSlice2 {
> fmt.Printf("vSlice2[%v] 值:%v 位址:%p\n", index, value, &vSlice2[index])
> }
> fmt.Println()
> fmt.Printf("vSlice3的位址 : %p\n", &vSlice3)
> for index, value := range vSlice3 {
> fmt.Printf("vSlice3[%v] 值:%v 位址:%p\n", index, value, &vSlice3[index])
> }
>
> //利用append函式拼接之新切片其元素所指向位址將與原先切片完全不同
> //(在切片長度超過原本的陣列長度狀況下)
> //意味著拼接之新切片與原先的舊切片記憶體位址完全不同
>
> copy(vSlice3, vSlice2)
> fmt.Println()
> fmt.Printf("vSlice3的位址 : %p\n", &vSlice3)
> for index, value := range vSlice3 {
> fmt.Printf("vSlice3[%v] 值:%v 位址:%p\n", index, value, &vSlice3[index])
> }
> fmt.Println(vSlice3)
> // copy前 vSlice2: [1 2 3 4 5 6 7 10 11 12 13]
> // copy前 vSlice3: [1 2 3 4 5 6 7 1 2 3 4 5 6 7 10 11 12 13]
> // copy後 vSlice3: [1 2 3 4 5 6 7 10 11 12 13 5 6 7 10 11 12 13]
>
> copy(vSlice2, vSlice3)
> fmt.Println(vSlice2)
> // copy前 vSlice2: [1 2 3 4 5 6 7 10 11 12 13]
> // copy後 vSlice3: [1 2 3 4 5 6 7 10 11 12 13]
>
> // copy切片長度即依序將元素覆蓋被copy之切片元素,直至切片長度結束為止,
> // copy完成後切片的長度將不會減少或是增加
>
>}
>
>```
### Go 切片作為可變參數(Variadic)驗證
> *可變參數實用性較
>``` go
>package main
>import "fmt"
>func main() {
> var a byte = 'a'
> var b = []byte{'b', 'b'}
> c := append([]byte{a}, b...)
> c = append(c, b...)
> fmt.Printf("%s", c)
> //abbbb
> }
>```
>
>```go
>package main
> import (
> "log"
> "time"
> )
>
> func StartTimer() time.Time {
> return time.Now()
> }
>
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var testCount = 9999999999
> sli := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
>
> time.Sleep(time.Second * 2)
> var vTime2 = StartTimer()
> for index := 0; index < testCount; index++ {
> add_slice(1, 2, sli)
> }
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime2))
> //2020/07/13 22:35:01 耗費:23448.0000 ms
>
> time.Sleep(time.Second * 2)
> var vTime1 = StartTimer()
> for index := 0; index < testCount; index++ {
> add_variadic(1, 2, sli...)
> }
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime1))
> //2020/07/13 22:35:53 耗費:49902.0000 ms
>
> }
>
> func add_slice(a, b int, nums []int) int {
> res := a + b
> if nums == nil {
> for _, num := range nums {
> res += num
> }
> }
> return res
> }
>
> func add_variadic(a, b int, nums ...int) int {
> res := a + b
> for _, num := range nums {
> res += num
> }
> return res
> }
> ```
### Go 切片複製方式驗證
>```go=
> package main
> import (
> "fmt"
> "log"
> "time"
> )
> var originalSlice []int
> const iterations = 10000
>
> func init() {
> for index := 0; index < iterations; index++ {
> originalSlice = append(originalSlice, index)
> }
> }
>
> func StartTimer() time.Time {
> return time.Now()
> }
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var vTime1 = StartTimer()
> for index := 0; index < iterations; index++ {
> CreatSliceWithAppend()
> }
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime1))
> //2021/11/08 21:44:37 耗費:693.3594 ms
> time.Sleep(time.Second * 1)
>
> var vTime2a = StartTimer()
> for index := 0; index < iterations; index++ {
> CreatSliceWithMakeIndex()
> }
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime2a))
> //2021/11/08 21:44:38 耗費:179.6875 ms
> time.Sleep(time.Second * 1)
>
> var vTime2b = StartTimer()
> for index := 0; index < iterations; index++ {
> CreatSliceWithMakeRange()
> }
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime2b))
> //2021/11/08 21:44:39 耗費:168.9453 ms
> time.Sleep(time.Second * 1)
>
> var vTime3 = StartTimer()
> var s3 []int
> for index := 0; index < iterations; index++ {
> s3 = append(s3, index)
> fmt.Sprintln(AppendVariable(s3, originalSlice))
> }
> s3 = nil
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime3))
> //2021/11/08 21:44:53 耗費:13563.4766 ms
> time.Sleep(time.Second * 1)
>
> var vTime4 = StartTimer()
> var s4 []int
> for index := 0; index < iterations; index++ {
> s4 = append(s4, index)
> fmt.Sprintln(AppendWithMake(s4, originalSlice))
> }
> s4 = nil
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime4))
> //2021/11/08 21:45:09 耗費:13940.4297 ms
> time.Sleep(time.Second * 1)
>
> var vTime5 = StartTimer()
> var s5 []int
> for index := 0; index < iterations; index++ {
> s5 = append(s5, index)
> fmt.Sprintln(AppendWithCopy(s5, originalSlice))
> }
> s5 = nil
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime5))
> //2021/11/08 21:45:23 耗費:13523.4375 ms
> time.Sleep(time.Second * 1)
>
> var vTime6 = StartTimer()
> var s6 []int
> for index := 0; index < iterations; index++ {
> s6 = append(s6, index)
> fmt.Sprintln(AppendWithAssign(s6, originalSlice))
> }
> s6 = nil
> log.Printf(" 耗費:%.4f ms ", SubTimer(vTime6))
> //2021/11/08 21:45:38 耗費:13794.9219 ms
> }
>
> func CreatSliceWithAppend() (outputtSlice []int) {
> for index := 0; index < iterations; index++ {
> outputtSlice = append(outputtSlice, index)
> }
> return
> }
>
> func CreatSliceWithMakeIndex() (outputtSlice []int) {
> outputSlice := make([]int, iterations)
> for index := 0; index < iterations; index++ {
> outputSlice[index] = index
> }
> return
> }
>
> func CreatSliceWithMakeRange() (outputtSlice []int) {
> outputSlice := make([]int, iterations)
> for index := range outputSlice {
> outputSlice[index] = index
> }
> return
> }
>
> func AppendVariable(inputSlice []int, appendSlice []int) (outputtSlice []int) {
> outputtSlice = append(inputSlice, appendSlice...)
> inputSlice = nil
> appendSlice = nil
> return
> }
>
> func AppendWithMake(inputSlice []int, appendSlice []int) (outputtSlice []int) {
> outputtSlice = make([]int, len(inputSlice)+len(appendSlice))
> outputtSlice = append(inputSlice, appendSlice...)
> inputSlice = nil
> appendSlice = nil
> return
> }
>
> func AppendWithCopy(inputSlice []int, appendSlice []int) (outputtSlice []int) {
> outputtSlice = make([]int, len(inputSlice)+len(appendSlice))
> copy(outputtSlice, inputSlice)
> copy(outputtSlice[len(inputSlice):], appendSlice)
> inputSlice = nil
> appendSlice = nil
> return
> }
>
> func AppendWithAssign(inputSlice []int, appendSlice []int) (outputtSlice []int) {
> outputtSlice = make([]int, len(inputSlice)+len(appendSlice))
> for index := 0; index < len(inputSlice); index++ {
> outputtSlice[index] = inputSlice[index]
> }
>
> for index := 0; index < len(appendSlice); index++ {
> outputtSlice[index+len(inputSlice)] = appendSlice[index]
> }
> inputSlice = nil
> appendSlice = nil
> return
> }
> ```
### Go 切片&陣列擴容[append()]陷阱 :
> 切片若引入原有的陣列當作元素後其使用append()函式擴容需要注意:\
> ==切片擴容未超過原先陣列長度元素,則所擴容的元素將會溢出並向下覆蓋原本的陣列元素==
> 可直接參考 : https://play.golang.org/p/2noxYSTVe__Y
>``` go
>package main
>import "fmt"
>func main() {
>
> //切片&陣列陷阱
> vArray1 := [...]int{0, 1, 2, 3, 4}
> vSlice1 := vArray1[1:3]
> fmt.Printf("原始宣告vArray1 = %v (%p)\n", vArray1, &vArray1)
> fmt.Printf("切片[0:2]vSlice1 = %v (%p)\n", vSlice1, &vSlice1)
> vSlice1 = append(vSlice1, 10)
> fmt.Printf("append 未超過原先陣列長度元素後↓ \n")
> fmt.Printf("append 後vSlice1 = %v (%p)\n", vSlice1, &vSlice1)
> fmt.Printf("append 後vSlice1[0] = %v (%p)\n", vSlice1[0], &vSlice1[0])
> fmt.Printf("append 後vSlice1[2] = %v (%p)\n", vSlice1[2], &vSlice1[2])
> fmt.Printf("原始宣告vArray1 = %v (%p)\n", vArray1, &vArray1)
> fmt.Printf("原始宣告vArray1[1] = %v (%p)\n", vArray1[1], &vArray1[1])
> fmt.Printf("原始宣告vArray1[3] = %v (%p)\n", vArray1[3], &vArray1[3])
> // 原始宣告vArray1 = [0 1 2 3 4] (0xc000068060)
> // 切片[0:2]vSlice1 = [1 2] (0xc000048420)
> // append 未超過原先陣列長度元素後↓
> // append 後vSlice1 = [1 2 10] (0xc000048420)
> // append 後vSlice1[0] = 1 (0xc000068068)
> // append 後vSlice1[2] = 10 (0xc000068078)
> // 原始宣告vArray1 = [0 1 2 10 4] (0xc000068060)
> // 原始宣告vArray1[1] = 1 (0xc000068068)
> // 原始宣告vArray1[3] = 10 (0xc000068078)
> // vSlice1藉由append 擴容未超過原先陣列長度元素,則所擴容的元素將會溢出並向下覆蓋原本的陣列元素
> // (0xc000068068)&(0xc000068078)=(0xc000068068)&(0xc000068078)
>
> fmt.Println()
> fmt.Println()
>
> //新手預想狀況
> vArray2 := [...]int{0, 1, 2, 3, 4}
> vSlice2 := vArray2[1:3]
> fmt.Printf("原始宣告vArray2 = %v (%p)\n", vArray2, &vArray2)
> fmt.Printf("切片[0:2]vSlice2 = %v (%p)\n", vSlice2, &vSlice2)
> vSlice2 = append(vSlice2, 10, 11, 12)
> fmt.Printf("append 超過原先陣列長度元素後↓ \n")
> fmt.Printf("append 後vSlice2 = %v (%p)\n", vSlice2, &vSlice2)
> fmt.Printf("append 後vSlice2[0] = %v (%p)\n", vSlice2[0], &vSlice2[0])
> fmt.Printf("append 後vSlice2[2] = %v (%p)\n", vSlice2[2], &vSlice2[2])
> fmt.Printf("原始宣告vArray2 = %v (%p)\n", vArray2, &vArray2)
> fmt.Printf("原始宣告vArray2[1] = %v (%p)\n", vArray2[1], &vArray2[1])
> fmt.Printf("原始宣告vArray2[3] = %v (%p)\n", vArray2[3], &vArray2[3])
>
> // 原始宣告vArray2 = [0 1 2 3 4] (0xc000068120)
> // 切片[0:2]vSlice2 = [1 2] (0xc0000484a0)
> // append 超過原先陣列長度元素後↓
> // append 後vSlice2 = [1 2 10 11 12] (0xc0000484a0)
> // append 後vSlice2[0] = 1 (0xc00006c0c0)
> // append 後vSlice2[2] = 10 (0xc00006c0d0)
> // 原始宣告vArray2 = [0 1 2 3 4] (0xc000068120)
> // 原始宣告vArray2[1] = 1 (0xc000068128)
> // 原始宣告vArray2[3] = 3 (0xc000068138)
> // vSlice2藉由append 擴容超過原先陣列長度元素,vSlice2切片所指向元素的位址將與原先完全不同
> // (0xc00006c0c0)&(0xc00006c0d0)≠(0xc000068128)&(0xc000068138)
>}
>```
>
### Go 切片排序(使用sort包):
```go=
package main
import (
"fmt"
"sort"
)
func main() {
var visl_Slice = []int{9, 1, 3, 5}
if !sort.IntsAreSorted(visl_Slice) {
sort.Ints(visl_Slice) //[1 3 5 9]
fmt.Println(visl_Slice)
sort.Sort(sort.Reverse(sort.IntSlice(visl_Slice)))
fmt.Println(visl_Slice) //[9 5 3 1]
}
var vfsl_Slice = []float64{9.9, 1.1, 3.3, 5.0}
if !sort.Float64sAreSorted(vfsl_Slice) {
sort.Float64s(vfsl_Slice) //[1.1 3.3 5 9.9]
fmt.Println(vfsl_Slice)
sort.Sort(sort.Reverse(sort.Float64Slice(vfsl_Slice)))
fmt.Println(vfsl_Slice) //[9.9 5 3.3 1.1]
}
var vssl_Slice = []string{"一", "b", "c", "二"}
if !sort.StringsAreSorted(vssl_Slice) {
sort.Strings(vssl_Slice) //[b c 一 二]
fmt.Println(vssl_Slice)
sort.Sort(sort.Reverse(sort.StringSlice(vssl_Slice)))
fmt.Println(vssl_Slice) //[二 一 c b]
}
}
```
### Go 切片&字串關係:
> 字串(String)在GoLang當中以唯讀byte型別的切片存在,相當於切片引入一個陣列所有元素皆是byte型別之常數(const),其中GoLang預設以UTF-8作預設編碼,每個字元(character)占用1~6個byte(無固定容量),故使用byte型別進行切片可能產生亂碼,需使用rune(int32)做切片才能取得正確的UTF-8編號。
>
> 切片的字串宣告方式(限用於宣告字串):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">切片名稱</span> = []<span style="color: lightgreen">字串型別</span>(<span style="color: yellow">字串</span>)**
> > *字串型別為 ```byte```、```int32```、```rune```
``` go
package main
import "fmt"
func main() {
vStr := "你好 Hello World!"
vSliceRuneStr := []rune(vStr)
vSliceInt32Str := []int32(vStr)
fmt.Printf("'%v'\n", vStr)
fmt.Println("字元長度(byte) : ", len(vStr))
fmt.Println("字元長度(rune) : ", len(vSliceRuneStr))
fmt.Println("字元長度(int32) : ", len(vSliceInt32Str))
fmt.Print("使用for 再以byte分解vStr : '")
for i := 0; i < len(vStr); i++ {
fmt.Printf("%c", vStr[i])
} //ä½ å¥½ Hello World!
fmt.Println("'")
fmt.Print("使用for-range分解vStr : '")
for _, vRange := range vStr {
fmt.Printf("%c", vRange)
} //你好 Hello World!
fmt.Println("'")
fmt.Print("使用for 分解vSliceRuneStr切片 : '")
for i := 0; i < len(vSliceRuneStr); i++ {
fmt.Printf("%c", vSliceRuneStr[i])
} //你好 Hello World!
fmt.Println("'")
fmt.Print("使用for 分解vSliceInt32Str切片 : '")
for i := 0; i < len(vSliceInt32Str); i++ {
fmt.Printf("%c", vSliceInt32Str[i])
} //你好 Hello World!
fmt.Println("'")
fmt.Println()
fmt.Printf("%v\n", []byte("你好 Hello World!"))
//將字元轉成Unicode編碼數字
fmt.Printf("%v\n", string([]byte{228, 189, 160, 229, 165, 189, 32, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33}))
//將Unicode編碼數字轉成字元
fmt.Print("使用for 再以string(byte)分解vStr : '")
for i := 0; i < len(vStr); i++ {
fmt.Printf("%v", string(vStr[i]))
} //ä½ å¥½ Hello World!
fmt.Println("'")
fmt.Println()
vSliceRuneStr[0] = '測'
vSliceRuneStr[1] = '試'
fmt.Println(vSliceRuneStr)
fmt.Println(string(vSliceRuneStr))
//[28204 35430 32 72 101 108 108 111 32 87 111 114 108 100 33]
//測試 Hello World!
vSliceInt32Str[0] = '成'
vSliceInt32Str[1] = '功'
fmt.Println(vSliceInt32Str)
fmt.Println(string(vSliceInt32Str))
// [25104 21151 32 72 101 108 108 111 32 87 111 114 108 100 33]
// 成功 Hello World!
}
```
### Go 雜湊表(Map)宣告
>
> 雜湊表(Map)是一種無排序鍵值(key-vaule)資料結構為引用型別,因該變數內的鍵值記憶體位址處於動態變化(鍵值位址無法獲取),必須先分配記憶體空間(手動或自動)後才能使用。分配完空間後往後Map鍵值增加會自動擴容,不會發生panic。
>
> Map的第一種宣告方式(僅能宣告Map名稱未分配記憶體空間):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">map名稱</span> map[<span style="color: lightgreen">鍵型別</span>]<span style="color: yellow">值型別</span>** \
> > *鍵的資料型別可為 ```邏輯值、數值、字串、指標、陣列、結構體、接口、通道```\
> > *值的資料型別可為任何資料型別包括map自己本身\
> > *該方式僅宣告Map名稱尚未分配記憶體,宣告完後無法直接使用(賦值、增加...)
>
> Map的第二種宣告方式(使用make()函式賦值,並分配記憶體空間):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">map名稱</span> = make(map[<span style="color: lightgreen">鍵型別</span>]<span style="color: yellow">值型別</span>,<span style="color: orange">map容量</span>])** \
> > *map容量省略或為空皆會預設為零值,該值必須為正整數
>
> Map的第三種宣告方式(直接賦予鍵值,將後自動分配記憶體空間)):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">map名稱</span> = map[<span style="color: lightgreen">鍵型別</span>]<span style="color: yellow">值型別</span>{<span style="color: lightgreen">鍵1</span>:<span style="color: yellow">值1</span>,<span style="color: lightgreen">鍵3</span>:<span style="color: yellow">值2</span>}**\
> > *鍵型別與值型別必須與key和value相符且一致\
> > *鍵名稱不能夠重複
> ``` go
> package main
> import "fmt"
> func main() {
>
> var vMap1 map[string]string
> vMap1 = make(map[string]string)
> vMap1["1A"] = "1aaAA"
> vMap1["1B"] = "1bbBB"
> fmt.Printf("vMap1 → %v (%p)", vMap1, &vMap1)
> fmt.Println()
>
> var vMap2 = make(map[string]string, 0)
> vMap2["2A"] = "2aaAA"
> vMap2["2B"] = "2bbBB"
> fmt.Printf("vMap2 → %v (%p)", vMap2, &vMap2)
> fmt.Println()
>
> var vMap3 = map[string]string{"3A": "3aaAA", "3B": "3bbBB"}
> fmt.Printf("vMap3 → %v (%p)\n", vMap3, &vMap3)
> fmt.Printf("vMap3[\"3B\"] = %v", vMap3["3B"])
> fmt.Println()
> fmt.Println()
>
> var vMap4 = make(map[string]map[string]string)
> vMap4["人名1"] = make(map[string]string)
> vMap4["人名1"]["體重"] = "10"
> vMap4["人名1"]["身高"] = "100"
> vMap4["人名2"] = make(map[string]string)
> vMap4["人名2"]["體重2"] = "20"
> vMap4["人名2"]["身高2"] = "200"
>
> fmt.Printf("vMap4 → %v (%p)\n", vMap4, &vMap4) //vMap4 → map[人名1:map[身高:100 體重:10] 人名2:map[身高2:200 體重2:20]] (0xc000080038)
> fmt.Println(vMap4["人名1"]) //map[身高:100 體重:10]
> fmt.Println(vMap4["人名2"]["身高2"]) //200
>
> }
> ```
>
### Go 雜湊表(map)使用(for-range)
Map因key與value值位置為無序,解析方式僅能使用for-range迴圈遍歷
```go
package main
import "fmt"
func main() {
var vMap1 = make(map[string]string)
vMap1["1A"] = "1aaAA"
vMap1["1B"] = "1bbBB"
for index, value := range vMap1 {
fmt.Printf("%v → %v\n", index, value)
}
fmt.Println()
//map值類型也可以為map自身,成為樹狀資料結構
var vMap2 = make(map[string]map[string]string)
vMap2["人名1"] = make(map[string]string)
vMap2["人名1"]["體重"] = "10"
vMap2["人名1"]["身高"] = "100"
vMap2["人名2"] = make(map[string]string)
vMap2["人名2"]["體重2"] = "20"
vMap2["人名2"]["身高2"] = "200"
for index1, value1 := range vMap2 {
fmt.Printf("%v\n", index1, value1)
for index2, value2 := range value1 {
fmt.Printf("%v → %v\n", index2, value2)
}
}
// 人名1 → map[身高:100 體重:10]
// 身高 → 100
// 體重 → 10
// 人名2 → map[身高2:200 體重2:20]
// 體重2 → 20
// 身高2 → 200
}
```
### Go 雜湊表(Map)次序問題
Map因key與value值位置為無序,可利用切片截取Map之key再利用sort()函式排序:
```go
package main
import (
"fmt"
"sort"
)
func main() {
var vMap1 = make(map[int]string)
vMap1[99] = "九九"
vMap1[9] = "九"
vMap1[1] = "一"
vMap1[10] = "十"
vMap1[2] = "二"
vMap1[11] = "十一"
delete(vMap1, 9)
fmt.Println(vMap1)
for index, value := range vMap1 {
fmt.Printf("vMap[%v] → %v\n", index, value)
}
//map[1:一 2:二 9:九 10:十 11:十一 99:九九]
//map因key與value值位置為無序,難以預測取值順序
fmt.Println()
fmt.Println()
//可創建一切片再利用sort排序
var vMap1_key []int
for index, _ := range vMap1 {
vMap1_key = append(vMap1_key, index)
}
sort.Ints(vMap1_key)
for _, value := range vMap1_key {
fmt.Printf("vMap[%v] → %v\n", value, vMap1[value])
}
fmt.Println()
fmt.Println()
// map[1:一 2:二 10:十 11:十一 99:九九]
// vMap[2] → 二
// vMap[11] → 十一
// vMap[99] → 九九
// vMap[1] → 一
// vMap[10] → 十
// vMap[1] → 一
// vMap[2] → 二
// vMap[10] → 十
// vMap[11] → 十一
// vMap[99] → 九九
sort.Sort(sort.Reverse(sort.IntSlice(vMap1_key)))
for _, value := range vMap1_key {
fmt.Printf("vMap[%v] → %v\n", value, vMap1[value])
}
// vMap[99] → 九九
// vMap[11] → 十一
// vMap[10] → 十
// vMap[2] → 二
// vMap[1] → 一
}
```
```go=
package main
import (
"fmt"
)
type ty_Custom struct {
F0 string
F1 string
}
func main() {
///////////////////////////////////////////////////////////////////////////////
var mst_Map1 = make(map[ty_Custom]map[string]string)
var st_Custom ty_Custom
st_Custom.F0 = "Tom"
st_Custom.F1 = "湯姆"
mst_Map1[st_Custom] = make(map[string]string)
mst_Map1[st_Custom]["衣服"] = "襯衫"
mst_Map1[st_Custom]["愛喝酒"] = "威士忌"
println(mst_Map1[st_Custom]["衣服"]) //襯衫
for index1, value1 := range mst_Map1 {
fmt.Printf("%v → %v\n", index1, value1)
for index2, value2 := range value1 {
fmt.Printf("%v → %v\n", index2, value2)
}
}
// {Tom 湯姆} → map[愛喝酒:威士忌 衣服:襯衫]
// 衣服 → 襯衫
// 愛喝酒 → 威士忌
println()
////////////////////////////////////////////////////////////////////////////////
var mst_Map2 = make(map[interface{}]map[string]string)
var if_Custom interface{}
mst_Map2[if_Custom] = make(map[string]string)
mst_Map2[if_Custom]["空值"] = "空值的字串"
println(mst_Map2[if_Custom]["空值"]) //空值的字串
println(mst_Map2[nil]["空值"]) //空值的字串
for index1, value1 := range mst_Map2 {
fmt.Printf("%v → %v\n", index1, value1)
for index2, value2 := range value1 {
fmt.Printf("%v → %v\n", index2, value2)
}
}
// <nil> → map[空值:空值的字串]
// 空值 → 空值的字串
println()
if_Custom = "anything"
println(mst_Map2[if_Custom]["空值"]) //nil
println(mst_Map2["anything"]["空值"]) //nil
println(mst_Map2[nil]["空值"]) //空值的字串
for index1, value1 := range mst_Map2 {
fmt.Printf("%v → %v\n", index1, value1)
for index2, value2 := range value1 {
fmt.Printf("%v → %v\n", index2, value2)
}
}
// <nil> → map[空值:空值的字串]
// 空值 → 空值的字串
println()
////////////////////////////////////////////////////////////////////////////////
var mst_Map3 = make(map[chan interface{}]map[string]string)
var ifch_Custom = make(chan interface{}, 4)
mst_Map3[ifch_Custom] = make(map[string]string)
ifch_Custom <- "anything"
ifch_Custom <- 1
ifch_Custom <- 2
ifch_Custom <- 3
mst_Map3[ifch_Custom]["CH-Key"] = "CH-Value"
println(mst_Map3[ifch_Custom]["CH-Key"]) //CH-Value
for index1, value1 := range mst_Map3 {
fmt.Printf("%v → %v\n", index1, value1)
for index2, value2 := range value1 {
fmt.Printf("%v → %v\n", index2, value2)
}
}
// 0xc00009e120 → map[CH-Key:CH-Value]
// CH-Key → CH-Value
}
```
### Go 切片雜湊表(map)型別(slice of map)
切片的資料型別可為map,可讓資料結構第一層計入編號,使用注意事項有:
1.切片map型別必須事先使用make()函式手動分配空間給(無法自動)
```go
package main
import "fmt"
func main() {
// 切片map型別必須手動分配容量,超過指定容量後將無法新增
var vSliceMap1 = make([]map[string]string, 2)
if vSliceMap1[0] == nil {
vSliceMap1[0] = make(map[string]string, 2)
vSliceMap1[0]["name"] = "N001"
vSliceMap1[0]["age"] = "99"
}
if vSliceMap1[1] == nil {
vSliceMap1[1] = make(map[string]string, 2)
vSliceMap1[1]["age"] = "100"
vSliceMap1[1]["name"] = "N002"
} //map的key為無序故難以預測排列狀況
fmt.Printf("%v", vSliceMap1)
fmt.Println()
//
var vSliceMap2 = make([]map[string]string, 0)
vSliceMap2 = append(vSliceMap2, map[string]string{"Name": "N01", "Age": "1"})
vSliceMap2 = append(vSliceMap2, map[string]string{"Name": "N02", "Age": "2"})
vSliceMap2 = append(vSliceMap2, map[string]string{"Name": "N03", "Age": "3"})
fmt.Printf("%v\n", vSliceMap2) //[map[age:99 name:N001] map[age:100 name:N002]]
fmt.Printf("%v\n", vSliceMap2[0]) //map[Age:1 Name:N01]
fmt.Println()
fmt.Println()
for index1 := 0; index1 < len(vSliceMap2); index1++ {
for index2, value := range vSliceMap2[index1] {
fmt.Printf("%v %v\n", index2, value)
}
} //使用切片map型別可依序將資料列出
// Name N01
// Age 1
// Name N02
// Age 2
// Name N03
// Age 3
}
```
### Go 結構體(Struct) 定義與宣告
>
> Golang支持物件導向程式設計(Object-oriented programming)特性,不使用其他語言常見的class指令,改使用結構體(Struct)來實現OO特性,GoLang與其他語言相比簡化了:繼承、重載(Overload)、構造函式(Constructor)、析構函式(Destructor)、隱藏參數this指針。結構體為值資料型別內可具備多種資料型別的屬性(Field),需用type指令定義其內的屬性,
>
> > **<span style="color: DeepSkyBlue">type </span><span style="color: DodgerBlue">結構體名稱</span> struct{**\
> >**<span style="color: lightgreen"> 屬性名稱1</span> <span style="color: yellow">屬性型別1</span>**\
> >**<span style="color: lightgreen"> 屬性名稱2</span> <span style="color: yellow">屬性型別2</span>**\
> > **}**
> >
> > *屬性的資料型別可為 ==邏輯值、數值、字串、指標、陣列、Map、結構體==\
> > *結構體的資料型別可為任何資料型別包括結構體自己本身 \
> > *定義完結構體後還須透過宣告才能成為變數
> 結構體的第一種宣告方式(僅宣告而未賦值):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">變數名稱</span> <span style="color: lightgreen">結構體名稱</span>**
>
> 結構體的第二種宣告方式(依序直接賦值):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">變數名稱</span> = <span style="color: lightgreen">結構體名稱</span>{<span style="color: yellow">屬性值1</span>,<span style="color: yellow">屬性值2</span>}**
>
> 結構體的第三種宣告方式(以key-value方式賦值):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">變數名稱</span> = <span style="color: lightgreen">結構體名稱</span>{<span style="color: yellow">屬名稱1</span>:<span style="color: orange">屬性值1</span>,<span style="color: yellow">屬性值2</span>:<span style="color: orange">屬性值2</span>}**
> >
> > *屬性名稱以key-value方式賦值,不需依照順序
> >
> 結構體的第四種宣告方式(使用new()宣告而未賦值):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">變數名稱</span> *<span style="color: lightgreen">結構體名稱</span> = new(<span style="color: lightgreen">結構體名稱</span>)**
> >
> > *==(*變數名稱).屬名稱 = 屬性值== 賦值等同 ==變數名稱.屬名稱 = 屬性值==
> >
> 結構體的第五種宣告方式(使用指標方式宣告並賦值):
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">變數名稱</span> *<span style="color: lightgreen">結構體名稱</span> = &<span style="color: lightgreen">結構體名稱</span>{<span style="color: yellow">屬名稱1</span>:<span style="color: orange">屬性值1</span>,<span style="color: yellow">屬性值2</span>:<span style="color: orange">屬性值2</span>}**
> >
> > *==(*變數名稱).屬名稱 = 屬性值== 賦值等同 ==變數名稱.屬名稱 = 屬性值==
> >
> ```go
> package main
> import "fmt"
> type TyStruct struct {
> F0 uint
> F1 string
> F2,F3 string //同類型可簡寫成一行
> }
>
> func main() {
>
> var vStructDF1 TyStruct
> vStructDF1.F0 = 1
> vStructDF1.F1 = "F1"
> vStructDF1.F2 = "F2"
> fmt.Printf("%T\nvStructDF1:\n%v\n%q\n%q\n%q", vStructDF1, vStructDF1.F0, vStructDF1.F1, vStructDF1.F2, vStructDF1.F3)
> fmt.Println()
> fmt.Println(&vStructDF1)
> fmt.Println()
>
> var vStructDF2 = TyStruct{1, "F1", "F2", ""}
> fmt.Printf("%T\nvStructDF2:\n%v\n%q\n%q\n%q", vStructDF2, vStructDF2.F0, vStructDF2.F1, vStructDF2.F2, vStructDF2.F3)
> fmt.Println()
> fmt.Println(&vStructDF2)
> fmt.Println()
>
> var vStructDF3 = TyStruct{F0: 1, F2: "F2", F1: "F1"}
> fmt.Printf("%T\nvStructDF3:\n%v\n%q\n%q\n%q", vStructDF3, vStructDF3.F0, vStructDF3.F1, vStructDF3.F2, vStructDF3.F3)
> fmt.Println()
> fmt.Println(&vStructDF3)
> fmt.Println()
>
> var vStructDF4 *TyStruct = new(TyStruct)
> (*vStructDF4).F0 = 1 //(*vStructDF4).F0 在這等同 vStructDF4.F0
> vStructDF4.F1 = "F1"
> vStructDF4.F2 = "F2"
>
> fmt.Printf("%T\nvStructDF4:\n%v\n%q\n%q\n%q", vStructDF4, vStructDF4.F0, vStructDF4.F1, vStructDF4.F2, vStructDF4.F3)
> fmt.Println()
> fmt.Println(*vStructDF4)
> fmt.Println()
>
> var vStructDF5 *TyStruct = &TyStruct{1, "F1", "F2", ""}
> (*vStructDF5).F0 = 2 //(*vStructDF5).F0 不可寫成 *vStructDF5.F0 因運算子優先不同
> vStructDF5.F1 = "F1-"
> vStructDF5.F2 = "F2-"
>
> fmt.Printf("%T\nvStructDF5:\n%v\n%q\n%q\n%q", vStructDF5, vStructDF5.F0, vStructDF5.F1, vStructDF5.F2, vStructDF5.F3)
> fmt.Println()
> fmt.Println(*vStructDF5)
>
> }
> ```
>
### Go 結構體與指標(Pointer to struct)
<img src="http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuSf9JIjHACbNACfCpoXHICaiIaqkoSpFu-BYAaXCpavCJrLGUh5YnhEsRIzNJr5IK4u1WsekJiwqeiJSL8MdwnO_dTZrVEk4PykxWIfUhEjwDdM7njCv6vxERR0og30mI4n9CJ3G16dWpCjFILLmB2fAhFJCAua6YN3gwe8fcoIxfnlNF9tG-tp5HaC5XQSDUwmKNYwOy6B5MY1J0IY0PXuklKReUZ6qMk3IQapGMaCKQz_lwKL3ZniQdknU-cpjafSBvpthMm7aOgf1Cu4C1D8Q2oHgkO282nQQ2ZQwkWhmM7ZMq_vinSpdireW8bnSoJc9nSKAE1KesDWeQ3Zev1SbPsIcQ2eeG3riQM1dfr3D1GecY0O5AmL45701hS_BBqbLoCzJoCdCorPGpYzAp4jFjK9F4n2zN0wfUId010S0" style="background-color:#2e2e2e;padding:0px;">
```go
package main
import "fmt"
type TyStruct1 struct {
F0 uint
F1 string
}
type TyStructUser struct {
start TyStructPoint
end TyStructPoint
}
type TyStructPoint struct {
x, y int
}
func main() {
var vStructDF1 TyStruct1
vStructDF1.F0 = 1
vStructDF1.F1 = "F1"
var vPointer *TyStruct1 = &vStructDF1
vPointer.F0 = 2
vPointer.F1 = "F11"
fmt.Printf("%v (%p)\n", vStructDF1, &vStructDF1) //{2 F11} (0xc00004a420)
fmt.Printf("%v (%p)\n", vPointer, &vPointer) //&{2 F11} (0xc000080018)
fmt.Printf("vPointer → %p\n", vPointer) //vPointer → 0xc00004a420
fmt.Println()
fmt.Printf("%v (%p)\n", vStructDF1.F0, &vStructDF1.F0) //2 (0xc00004a420)
fmt.Printf("%v (%p)\n", vStructDF1.F1, &vStructDF1.F1) //F11 (0xc00004a428)
fmt.Println()
var vStructDF2 = TyStructUser{TyStructPoint{1, 2}, TyStructPoint{3, 4}}
fmt.Printf("vStructDF2 (%p)\n", &vStructDF2) //vStructDF2 (0xc000052140)
fmt.Printf("Start Point = (%v,%v) (%p,%p)\n",
vStructDF2.start.x, vStructDF2.start.y,
&vStructDF2.start.x, &vStructDF2.start.y)
//Start Point = (1,2) (0xc000052140,0xc000052148)
fmt.Printf("End Point = (%v,%v) (%p,%p)\n",
vStructDF2.end.x, vStructDF2.end.y,
&vStructDF2.end.x, &vStructDF2.end.y)
//End Point = (1,2) (0xc000052140,0xc000052148)
}
```
### Go 結構體與打包問題(Package struct)
``` Package_Struct/PackageStruct.go```
```go
package PackageStruct
type GlobalTyStruct struct {
F0, F1, F2, F3 string
}
type GlobalButLocalTyStruct struct {
f0, f1, f2, f3 string
}
type localTyStruct struct {
f0, f1, f2, f3 string
}
type localButGlobalTyStruct struct {
F0, F1, F2, F3 string
}
func LocalExportPort(InputF0 string, InputF1 string, InputF2 string, InputF3 string) *localTyStruct {
return &localTyStruct{f0: InputF0, f1: InputF1, f2: InputF2, f3: InputF3}
}
```
``` main.go```
```go
package main
import (
PackageStruct "Lesson3/Struct與打包/Package_Struct"
"fmt"
)
func main() {
var vStructDF1 = PackageStruct.GlobalTyStruct{F0: "F0", F1: "F1", F2: "F2"}
fmt.Println(vStructDF1)
fmt.Println()
// var vStructDF2 = PackageStruct.GlobalButLocalTyStruct{F0: "F0", F1: "F1", F2: "F2"}
// fmt.Println(vStructDF2) //unexported
// fmt.Println()
// var vStructDF3 = PackageStruct.localTyStruct{F0: "F0", F1: "F1", F2: "F2"}
// fmt.Println(vStructDF3) //unexported
// fmt.Println()
// var vStructDF4 = PackageStruct.localButGlobalTyStruct{F0: "F0", F1: "F1", F2: "F2"}
// fmt.Println(vStructDF4) //unexported
// fmt.Println()
fmt.Println(*PackageStruct.LocalExportPort("funcF0", "funcF1", "funcF2", ""))
fmt.Println()
}
```
### Go 結構體雜湊表(map)型別(struct of map)
```go
package main
import "fmt"
type st_Example struct {
id uint64
name string
data interface{}
}
func FC_newStruct(id uint64, name string, data interface{}) *st_Example {
return &st_Example{id, name, data}
}
func (r *st_Example) Update(id uint64, name string, data interface{}) {
r.id = id
r.name = name
r.data = data
return
}
func main() {
var ex1 *st_Example
ex1 = FC_newStruct(1, "name1", "data1")
fmt.Println(ex1, ex1.name) //&{1 name1 data1} name1
var ex2 st_Example //struct必須作為變數必須是實參
ex2.Update(2, "name2", "data2")
fmt.Println(ex2, ex2.name) //{2 name2 data2} name2
var mpExample = make(map[string]*st_Example)
mpExample["key"] = &st_Example{}
//Update前必須存入struct
mpExample["key"].Update(91, "name91", "data91")
fmt.Println(mpExample["key"], mpExample["key"].name) //&{92 name92 data92} name92
delete(mpExample, "key")
mpExample["key"] = &st_Example{0, "name92", "data92"}
mpExample["key"].id = 92
fmt.Println(mpExample["key"], mpExample["key"].name) //&{91 name91 data91} name91
//map-*struct寫入非常簡便
}
```
### Go 方法與函式 (Method&Function)
> 1. 函式使用方式為 ``` 函式名(變數列表) ```,方法使用方式為 ``` 變數.方法名(變數列表) ```
> 2. 當資料型別為值型別時,函式(Function)不能直接將指標當作變數輸入,反之方法(Method)可將變數與指標當作自訂型別變數直接輸入,方法(Method)可定義自訂型別變數影響範圍
>
> Method宣告方式:
> > **<span style="color: DeepSkyBlue">func </span>(<span style="color: DodgerBlue">自訂型別名稱</span> <span style="color: DeepSkyBlue">自訂型別</span>) <span style="color: lightgreen">方法名稱</span>(<span style="color: yellow">輸入變數列表</span>) (<span style="color: orange">輸出變數列表</span>) {\
> ><span style="color: red"> 方法內容...</span>\
> > return <span style="color: orange">輸出值</span>\
> > }**
> >
> > *輸入、輸出變數非必需的,有輸出變數則一定要使用 ``` return ``` 返回值\
> > *方法(Method)必須綁定於自訂型別,必須有自訂型別變數才能使用\
> > *編譯器對於 ```(&變數名稱).方法名稱()``` 與 ``` 變數名稱.方法名稱()``` 兩者等價\
> > *方法之自訂型別可定義 ```引用或值型別```,設定其作用範圍(不因指標而影響)
>
> ```go
> package main
> import "fmt"
>
> func fcInputString(input string) {
> input = "Changed"
> fmt.Printf("fcInputString = %v\n", input)
> }
>
> func fcInputPString(input *string) {
> *input = "Changed"
> fmt.Printf("fcInputPString = %v\n", *input)
> }
>
> type TyInput string
>
> func (input TyInput) mtInputString() {
> input = "Changed"
> fmt.Printf("mtInputString = %v\n", input)
> }
>
> func (input *TyInput) mtInputPString() {
> *input = "Changed"
> fmt.Printf("mtInputPString = %v\n", *input)
> }
>
> func main() {
>
> var vString1 string
>
> vString1 = "未改變"
> fcInputString(vString1)
> fmt.Printf("檢查vString1 = %v\n", vString1)
> fmt.Println()
> vString1 = "未改變"
> fcInputPString(&vString1)
> fmt.Printf("檢查vString1 = %v\n", vString1)
> // fcInputString = Changed
> // 檢查vString1 = 未改變
> // fcInputPString = Changed
> // 檢查vString1 = Changed
> // func傳入之值型別變數影響範圍僅在func內(除非使用全域變數)
> // func若傳入指標則影響範圍可直接改寫該指標指定的變數
>
> fmt.Println()
> fmt.Println()
> var vString2 TyInput
>
> vString2 = "未改變"
> vString2.mtInputString()
> fmt.Printf("檢查vString1 = %v\n", vString2)
> fmt.Println()
> vString2 = "未改變"
> (&vString2).mtInputString()
> fmt.Printf("檢查vString1 = %v\n", vString2)
> // mtInputString = Changed
> // 檢查vString1 = 未改變
> // mtInputString = Changed
> // 檢查vString1 = 未改變
> // 因編譯器中(&vString2).mtInputString()與 vString2.mtInputString()等價
> // Method傳入之自訂型別可藉由指定值型別,將影響範圍僅在func內(除非使用全域變數)
>
> fmt.Println()
> fmt.Println()
> vString2 = "未改變"
> vString2.mtInputPString()
> fmt.Printf("檢查vString1 = %v\n", vString2)
> fmt.Println()
> vString2 = "未改變"
> (&vString2).mtInputPString()
> fmt.Printf("檢查vString1 = %v\n", vString2)
> // mtInputPString = Changed
> // 檢查vString1 = Changed
> // mtInputPString = Changed
> // 檢查vString1 = Changed
> // 因編譯器中(&vString2).mtInputString()與 vString2.mtInputString()等價
> // Method傳入之自訂型別可藉由指定引用型別,將影響範圍擴展至func外
> }
> ```
### Go 方法與結構體應用 (Method&struct):
```go
package main
import (
"fmt"
)
const pi = 3.14159265359
type Radius struct {
Radius float64
}
func (r Radius) mtAreaV() float64 {
return pi * r.Radius * r.Radius
}
func (r *Radius) mtAreaR() float64 {
return pi * r.Radius * r.Radius
}
func main() {
var vR = Radius{10}
fmt.Printf("%v", vR.mtAreaV())
fmt.Println()
fmt.Printf("%v", vR.mtAreaR())
//雖然兩者方法輸出相同,但引用型別在處理上所耗費記憶體較低(因省略變數值的複製)
}
```
### Go 方法調用驗證性能:
```go=
package main
import (
"fmt"
"log"
"reflect"
"time"
)
///////////////////////////////////////////
func StartTimer() time.Time {
return time.Now()
}
func SubTimer(input time.Time) float64 {
return time.Now().Sub(input).Seconds() * 1000
}
/////////////////////////////////////////
type ty_Json struct {
Name string `json:"jsname"`
Age int `json:"jsage"`
}
func main() {
st_User := ty_Json{
"測試",
20,
}
var TestCount = 99999
var vTime1 = StartTimer()
for index := 0; index < TestCount; index++ {
for index := 0; index < reflect.ValueOf(st_User).NumField(); index++ {
if reflect.ValueOf(st_User).Field(index).CanInterface() { //判斷是否可導出屬性
fmt.Sprintf("%v (%v %v %v)\n",
reflect.ValueOf(st_User).Field(index).Interface(),
reflect.TypeOf(st_User).Field(index).Name,
reflect.TypeOf(st_User).Field(index).Type,
reflect.TypeOf(st_User).Field(index).Tag)
}
}
}
log.Printf(" 耗費:%.4f ms ", SubTimer(vTime1))
//2019/09/30 15:22:26 耗費:433.0247 ms
v := reflect.ValueOf(st_User)
t := reflect.TypeOf(st_User)
var vTime2 = StartTimer()
for index := 0; index < TestCount; index++ {
for index := 0; index < v.NumField(); index++ {
if v.Field(index).CanInterface() { //判斷是否可導出屬性
fmt.Sprintf("%v (%v %v %v)\n",
v.Field(index).Interface(),
t.Field(index).Name,
t.Field(index).Type,
t.Field(index).Tag)
}
}
}
log.Printf(" 耗費:%.4f ms ", SubTimer(vTime2))
//2019/09/30 15:22:26 耗費:195.0111 ms
//
//宣告變數進行方法調用效能較佳
}
```
### Go 方法String()使用特例:
```go
package main
import (
"fmt"
)
type tyID struct {
id int
name string
}
func (vID *tyID) String() string {
return fmt.Sprintf("log: id=%v name=%v", (*vID).id, (*vID).name)
}
func (vID *tyID) Test() string {
return fmt.Sprintf("log: id=%v name=%v", (*vID).id, (*vID).name)
}
func main() {
var dataID tyID
dataID.id = 1
dataID.name = "User001"
fmt.Println(&dataID) //log: id=1 name=User001
fmt.Println(dataID.Test()) //log: id=1 name=User001
//String()將會作為fmt.Println與fmt.Printf的預設輸出格式
//相比Test()可以簡化指令
}
```
### Go 方法重寫與繼承:
```go=
package main
import (
"fmt"
)
type animal struct {
name string
spark string
}
type dog struct {
animal //繼承(Inheritance)
age uint
}
type cat struct {
animal
age int
}
func (ty_input animal) Spark() { //重寫(Override)
fmt.Printf("%v → 任何動物都會叫 :「 %v」 \n", ty_input.name, ty_input.spark)
}
func (ty_input dog) Spark() {
fmt.Printf("%v → %v歲 的狗會汪汪叫 :「 %v」 \n", ty_input.name, ty_input.age, ty_input.spark)
}
func main() {
st_Animal := animal{"Unknow", "Wu Wu"}
st_Animal.Spark() //Unknow → 任何動物都會叫 :「 Wu Wu」
dog := dog{animal{"土豆", "wang~ wang~ wang~"}, 4}
dog.Spark() //土豆 → 4歲 的狗會汪汪叫 :「 wang~ wang~ wang~」
cat := cat{animal{"小黑", "喵~ 喵~"}, 4}
cat.Spark() //小黑 → 任何動物都會叫 :「 喵~ 喵~」
}
```
### Go 嵌入結構體組合型別(Embedded Struct Field):
Golang沒有類(class),Go語言的結構體(struct)和其它程式語言的類(class)有同等的地位,可以理解Golang是基於struct來實現OOP繼承(Inheritance)特性。
1.結構體嵌入了另一個結構體並且定義該屬性名稱,稱為「嵌入具名結構體組合型別」\
2.結構體嵌入了另一個結構體並且無定義該屬性名稱,稱為「嵌入匿名結構體組合型別」
``` Package_Struct/PackageStruct.go```
```go
package PackageStruct
type NDUser struct {
ID uint
Name string
NData NDUserdata //具名屬性
}
type NDUserdata struct {
Level uint
Age uint
}
type AyUser struct {
ID uint
Name string
AyUserdata //匿名屬性
}
type AyUserdata struct {
Level uint
Age uint
}
```
``` main.go```
``` go
package main
import (
PackageStruct "Lesson3/Struct組合型別(具名vs匿名)/Package_Struct"
"fmt"
)
type NDUser struct {
ID uint
Name string
NData NDUserdata
}
type NDUserdata struct {
Level uint
Age uint
}
type AyUser struct {
ID uint
Name string
AyUserdata
}
type AyUserdata struct {
Level uint
Age uint
}
func main() {
//////////////////////////////////////////////////////////////////////////////
var tyNewNDUser = NDUser{
ID: 1, Name: "NDUser001",
NData: NDUserdata{Level: 0, Age: 11},
}
fmt.Printf("嵌入具名結構體組合型別 : \n%v", tyNewNDUser)
fmt.Println()
var tyNewAyUser = AyUser{
ID: 1, Name: "AyUser001",
AyUserdata: AyUserdata{Level: 0, Age: 11},
}
fmt.Printf("嵌入匿名結構體組合型別 : \n%v", tyNewAyUser)
fmt.Println()
fmt.Println()
// 嵌入具名結構體組合型別 :
// {1 NDUser001 {0 11}}
// 嵌入匿名結構體組合型別 :
// {1 AyUser001 {0 11}}
//////////////////////////////////////////////////////////////////////////////
var tyPKNewAyUser = PackageStruct.AyUser{
ID: 2, Name: "AyUser002",
AyUserdata: PackageStruct.AyUserdata{Level: 1, Age: 22},
}
fmt.Printf("嵌入具名結構體組合型別(引入包) : \n%v", tyPKNewAyUser)
fmt.Println()
var tyPKNewNDUser = PackageStruct.NDUser{
ID: 2, Name: "NDUser002",
NData: PackageStruct.NDUserdata{Level: 1, Age: 22},
}
fmt.Printf("嵌入匿名結構體組合型別(引入包) : \n%v", tyPKNewNDUser)
fmt.Println()
// 嵌入具名結構體組合型別(引入包) :
// {2 AyUser002 {1 22}}
// 嵌入匿名結構體組合型別(引入包) :
// {2 NDUser002 {1 22}}
}
```
3.結構體嵌入了多個匿名結構體,可透過這個結構直接訪問多個匿名結構體,以實現多重繼承
```go
package main
import "fmt"
type Camera struct {
Name string
}
func (_ *Camera) takePicture() string {
return "Click"
}
type Phone struct {
Name string
}
func (_ *Phone) call() string {
return "Ring Ring"
}
type CameraPhone struct {
Name string
Camera
Phone
}
func main() {
var vDevice1 = Camera{Name: "NewCamera"}
fmt.Printf("%v : %v\n", vDevice1.Name, vDevice1.takePicture())
var vDevice2 = CameraPhone{Name: "NewCameraPhone", Camera: Camera{Name: "NewCamera"}}
//vDevice2.Camera.Name = "NewCamera" //左段語法較為簡潔實現匿名屬性之結構體組合
vDevice2.Phone.Name = "NewPhone"
fmt.Printf("%v : %v → %v\n", vDevice2.Name, vDevice2.Phone.Name, vDevice2.Phone.call())
fmt.Printf("%v : %v → %v\n", vDevice2.Name, vDevice2.Camera.Name, vDevice2.Camera.takePicture())
fmt.Printf("%v : %v → %v\n", vDevice2.Name, vDevice2.Phone.Name, vDevice2.call())
fmt.Printf("%v : %v → %v\n", vDevice2.Name, vDevice2.Camera.Name, vDevice2.takePicture())
//多個匿名結構體所形成多重繼承,編譯器會自動搜尋關鍵字簡化語法
// NewCamera : Click
// NewCameraPhone : NewPhone → Ring Ring
// NewCameraPhone : NewCamera → Click
// NewCameraPhone : NewPhone → Ring Ring
// NewCameraPhone : NewCamera → Click
}
```
### Go 接口定義(Interface):
> Go的官方文件並未明確指出接口(Interface)動態、靜態的型別分類(Dynamic、Static),但《Go语言核心编程》(李文塔) 的分析指出接口可由Duck型別做出動靜態分類 (https://blog.golang.org/laws-of-reflection)
接口為引用型別之多態變數,它可隨實踐方法(Method)的不同定義各種變數型態(type)的行為。舉例如下宣告一空接口並賦值,其輸出結果之變數型態將隨賦值而改變(相當於使用 ``` var := value``` 的效果),
```go=
package main
import (
"fmt"
)
type Phone interface {
Who()
}
type NokiaPhone struct {
}
func (_ *NokiaPhone) Who() {
fmt.Println("I am Nokia!")
}
type ApplePhone struct {
}
func (_ *ApplePhone) Who() {
fmt.Println("I am ApplePhone!")
}
func main() {
var vP Phone
vP = &NokiaPhone{}
vP.Who()
//I am Nokia!
vP = &ApplePhone{}
vP.Who()
//I am ApplePhone!
}
```
```go=
package main
type if_A interface {
mt1()
}
type if_B interface {
if_A
mt2()
}
type if_C interface {
String()
}
type ty_Example struct{}
func (ty_Example) mt1() {
println("use mt1")
}
func (ty_Example) mt2() {
println("use mt2")
}
func main() {
var st_Exampe = ty_Example{}
st_Exampe.mt1() //use mt1
st_Exampe.mt2() //use mt2
println()
var if_ty_Example interface{} = ty_Example{}
if TypeAssertion, check := if_ty_Example.(if_A); check {
TypeAssertion.mt1()
//TypeAssertion.mt2() //if_A接口無mt2()之方法
} //use mt1
if TypeAssertion, check := if_ty_Example.(if_B); check {
TypeAssertion.mt1()
TypeAssertion.mt2()
}
//use mt1
//use mt2
if TypeAssertion, check := if_ty_Example.(if_C); check {
println(TypeAssertion) //此段亦不會執行,因if_C斷言失敗
}
TypeAssertion := if_ty_Example.(if_C) //panic: interface conversion: main.ty_Example is not main.if_C: missing method String
println(TypeAssertion) //此段亦不會執行,因if_C斷言失敗
}
```
### Go 接口型別斷言與型別查詢(Type assertions&Switches):
```go=
package main
import "fmt"
func main() {
var vInterface interface{}
fcDisplay(vInterface)
// Value='<nil' Type='<nil')
if vInterface == vInterface {
println("相等")
}
vInterface = 0
fcDisplay(vInterface)
// Value='0' Type='int')
vInterface = -1
fcDisplay(vInterface)
// Value='-1' Type='int')
vInterface = 1.1
fcDisplay(vInterface)
// Value='1.1' Type='float64')
vInterface = 10e10
fcDisplay(vInterface)
// Value='1e+11' Type='float64')
vInterface = 'c'
fcDisplay(vInterface)
// Value='99' Type='int32')
vInterface = '北'
fcDisplay(vInterface)
// Value='21271' Type='int32')
vInterface = "ssss"
fcDisplay(vInterface)
// Value='ssss' Type='string')
vInterface = [...]int{1, 2, 3}
fcDisplay(vInterface)
// Value='[1 2 3]' Type='[3]int')
vInterface = []int{1, 2, 3}
fcDisplay(vInterface)
// Value='[1 2 3]' Type='[]int')
vInterface = func(input int) int { return input }
fcDisplay(vInterface)
// Value='0x493350' Type='func(int) int')
vInterface = map[string]string{"A": "aaAA", "B": "bbBB"}
fcDisplay(vInterface)
// Value='map[A:aaAA B:bbBB]' Type='map[string]string')
vInterface = TyStruct{0}
fcDisplay(vInterface)
// Value='{0}' Type='main.TyStruct')
vInterface = &TyStruct{0}
fcDisplay(vInterface)
//Value='&{0}' Type='*main.TyStruct')
vInterface2 := &TyStruct{0}
fmt.Println(vInterface == vInterface2)
//false
//介面作為實值型別變數皆可做邏輯判斷,參考型別除指標以外其餘皆不行
// var vInterface3 interface{}
// vInterface3 = []int{1, 2, 3}
// if vInterface3 == vInterface3 {
// println("相等")
// } //runtime error: comparing uncomparable type []int
// 空接口斷言為參考型別則不能做邏輯比較
}
type TyStruct struct {
F0 uint
}
func fcDisplay(this interface{}) {
fmt.Printf("Value='%v' Type='%T')\n", this, this)
}
```
```go=
package main
import "fmt"
func fc_BaseTypeAssertion(emif interface{}) {
switch value := emif.(type) {
case bool:
fmt.Printf("bool: %v(%T)\n", value, value)
case complex128:
fmt.Printf("complex128: %v(%T)\n", value, value)
case complex64:
fmt.Printf("complex64: %v(%T)\n", value, value)
case float32:
fmt.Printf("float32: %v(%T)\n", value, value)
case float64:
fmt.Printf("float64: %v(%T)\n", value, value)
case int:
fmt.Printf("int: %v(%T)\n", value, value)
case int16:
fmt.Printf("int16: %v(%T)\n", value, value)
case int32:
fmt.Printf("int32: %v(%T)\n", value, value)
case int64:
fmt.Printf("int64: %v(%T)\n", value, value)
case int8:
fmt.Printf("int8: %v(%T)\n", value, value)
case string:
fmt.Printf("string: %v(%T)\n", value, value)
case uint:
fmt.Printf("uint: %v(%T)\n", value, value)
case uint16:
fmt.Printf("uint16: %v(%T)\n", value, value)
case uint32:
fmt.Printf("uint32: %v(%T)\n", value, value)
case uint64:
fmt.Printf("uint64: %v(%T)\n", value, value)
case uint8:
fmt.Printf("uint8: %v(%T)\n", value, value)
case uintptr:
fmt.Printf("uintptr: %v(%T)\n", value, value)
default:
fmt.Printf("%v(%T)\n", value, value)
}
}
func main() {
fc_BaseTypeAssertion(nil)
fc_BaseTypeAssertion(1)
var vu64 uint64 = 18446744073709551615
fc_BaseTypeAssertion(vu64)
fc_BaseTypeAssertion("xxxString")
fc_BaseTypeAssertion(&vu64)
fcA := func(x int) {
println(x)
}
fc_BaseTypeAssertion(fcA)
//<nil>(<nil>)
// int: 1(int)
// uint64: 18446744073709551615(uint64)
// string: xxxString(string)
// 0xc0000540b8(*uint64)
// 0x493ed0(func(int))
}
```
### Go 管道(channel)宣告與使用
> 管道(channel)作為Go的一引用型資料型別,可以存放(寫入)任何資料型別作為數據(元素)保存,其特點為取出管道內元素必須遵守「**依序先進先出**」、「**進入管道後不可修改或截取**」、「**管道關閉後只能取出不能再寫入**」,管道必須透過 ``make`` 函式創建:
>
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">管道名稱</span> = make(chan <span style="color: lightgreen">元素型別</span>,<span style="color: yellow">元素容量</span>)**
>
> 管道可透過 ``<-`` 符號寫入值或取出元素,也可作為宣告該管道限定寫入或取出,期宣告方式如下:
>
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">限寫管道名稱</span> = make(chan <- <span style="color: lightgreen">元素型別</span>,<span style="color: yellow">元素容量</span>)**
> > **<span style="color: DeepSkyBlue">var </span><span style="color: DodgerBlue">限取管道名稱</span> = make(<-chan <span style="color: lightgreen">元素型別</span>,<span style="color: yellow">元素容量</span>)**
> >
> > *宣告管道時若無限定寫入或取出,即表示該管道是雙向的(可寫入和取出)\
> > *管道元素型別相同則視為相同型別,不受限定寫入或取出影響\
> > *雙向的管道可轉換為限定寫入或取出(單向);單向的管道後不可轉換成雙向或異相;
>
> ```go=
> package main
> import "fmt"
> func main() {
>
> var ifch_Test = make(chan interface{}, 10)
>
> fmt.Printf("%v(%T)\n", ifch_Test, ifch_Test) //0xc000088000(chan interface {})
>
> fmt.Printf("%v\n", len(ifch_Test)) //0
> ifch_Test <- "jack"
> fmt.Printf("%v\n", len(ifch_Test)) //1
> ifch_Test <- 2
> fmt.Printf("%v\n", len(ifch_Test)) //2
> type ty_Example struct {
> F0 string
> F1 string
> }
> ifch_Test <- ty_Example{F0: "屬性值0", F1: "屬性值1"}
> fmt.Printf("%v/%v\n", len(ifch_Test), cap(ifch_Test)) //3/10
> var if_empty interface{}
> ifch_Test <- if_empty
> fmt.Printf("%v/%v\n", len(ifch_Test), cap(ifch_Test)) //4/10
>
> var if_cache interface{}
> if_cache = <-ifch_Test
> fmt.Printf("%v(%T)\n", if_cache, if_cache) //jack(string)
> if_cache = <-ifch_Test
> fmt.Printf("%v(%T)\n", if_cache, if_cache) //2(int)
> if_cache = <-ifch_Test
> fmt.Printf("%v(%T)\n", if_cache, if_cache) //{屬性值0 屬性值1}(main.ty_Example)
> fmt.Printf("%v(%T)\n", if_cache.(ty_Example).F0, if_cache.(ty_Example).F0) //屬性值0(string),對空接口之結構體型別斷言
> if_cache = <-ifch_Test
> fmt.Printf("%v(%T)\n", if_cache, if_cache) //<nil>(<nil>) 回傳空接口
> if_cache = 10
> fmt.Printf("%v(%T)\n", if_cache, if_cache) //10(int) 對空接口賦值同時斷言
>
> // if_cache = <-ifch_Test //channel提取為空而導致錯誤
> // fmt.Printf("%v(%T)\n", if_cache, if_cache)
>
> }
>
> ```
>
> ``` go=
> package main
>
> import (
> "fmt"
> )
>
> func main() {
>
> vsch_1 := make(chan string)
> vsch_2 := make(chan string)
>
> for index := 0; index < 100; index++ {
> go func() {
> vsch_1 <- "one"
> }()
> go func() {
> vsch_2 <- "two"
> }()
>
> for i := 0; i < 2; i++ {
> select {
> case select1 := <-vsch_1:
> fmt.Println("received:", select1)
> case select2 := <-vsch_2:
> fmt.Println("received:", select2)
> default:
> fmt.Println("default")
> }
> }
> }
>
> // default
> // default
> // default
> // received: one
> // received: one
> // received: one
> // received: one
> // received: one
> // received: one
> // received: two
> // received: two
> // received: two
> // received: one
> // ...... 隨機選擇通道
> }
>
> ```
### Go 管道(channel)型別驗證:
> ```go=
> package main
>
> import "fmt"
>
> type intChan chan int
> type intWChan chan<- int
>
> func main() {
> ch01 := make(chan int) // 0xc00003e060 (chan int)
> ch02 := make(chan int) // 0xc00003e0c0 (chan int)
> if ch01 != ch02 {
> fmt.Printf("%v != %v\n", ch01, ch02)
> } //0xc00003e060 != 0xc00003e0c0
> fmt.Println()
>
> chSameType1 := (chan int)(ch01)
> chSameType2 := (chan int)(chSameType1)
> chSameType1 = chSameType2 // 0xc00003e060 (chan int)
> chSameType2 = ch02 // 0xc00003e0c0 (chan int)
>
> chCustomType := (intChan)(ch01)
> chCustomType = ch02 // 0xc00003e0c0 (main.intChan)
> //同型別可直接賦值,也可轉換成相同型別(包括自定型別)
>
> fmt.Printf("%v (%T)\n", ch01, ch01) // 0xc00003e060 (chan int)
> fmt.Printf("%v (%T)\n", ch02, ch02) // 0xc00003e0c0 (chan int)
> fmt.Printf("%v (%T)\n", chSameType1, chSameType1) // 0xc00003e060 (chan int)
> fmt.Printf("%v (%T)\n", chSameType2, chSameType2) // 0xc00003e0c0 (chan int)
> fmt.Printf("%v (%T)\n", chCustomType, chCustomType) // 0xc00003e0c0 (main.intChan)
> fmt.Println()
>
> chWriteOnly1 := (chan<- int)(ch01)
> chTakeOnly1 := (<-chan int)(ch01)
>
> //chWriteOnly2 := (chan int)(chWriteOnly1) //cannot convert chWriteOnly1 (type chan<- int) to type chan int
> //chTakeOnly2 := (chan int)(chWriteOnly1) //cannot convert chWriteOnly1 (type chan<- int) to type chan int
> //chWriteOnly2 = (chan<- int)(chTakeOnly1) //cannot convert chTakeOnly1 (type <-chan int) to type chan<- int
> //chTakeOnly2 := (<-chan int)(chWriteOnly1) //cannot convert chWriteOnly1 (type chan<- int) to type <-chan int
>
> chWriteOnly2 := (chan<- int)(ch01)
> chTakeOnly2 := (<-chan int)(ch01)
> chConvertCheck := (intWChan)(chWriteOnly2)
> fmt.Printf("%v (%T)\n", chConvertCheck, chConvertCheck) // 0xc00003e060 (main.intWChan)
> fmt.Println()
>
> fmt.Printf("%v (%T)\n", chWriteOnly2, chWriteOnly2) // 0xc00003e060 (chan<- int)
> fmt.Printf("%v (%T)\n", chTakeOnly2, chTakeOnly2) // 0xc00003e060 (<-chan int)
> fmt.Println()
>
> fmt.Printf("%v (%T)\n", chWriteOnly1, chWriteOnly1) // 0xc00003e060 (chan<- int)
> fmt.Printf("%v (%T)\n", chTakeOnly1, chTakeOnly1) // 0xc00003e060 (<-chan int)
> chWriteOnly1 = ch01
> chTakeOnly1 = ch01
> fmt.Printf("%v (%T)\n", chWriteOnly1, chWriteOnly1) // 0xc00003e060 (chan<- int)
> fmt.Printf("%v (%T)\n", chTakeOnly1, chTakeOnly1) // 0xc00003e060 (<-chan int)
> fmt.Printf("%v (%T)\n", ch01, ch01) // 0xc00003e060 (chan int)
> //chWriteOnly = chTakeOnly //指向不同無法直接賦值 : cannot use chTakeOnly (type <-chan int) as type chan<- int in assignment
>
> func01(chWriteOnly1, chTakeOnly1)
> // 0xc00003e060 (chan<- int)
> // 0xc00003e060 (<-chan int)
> func01(ch01, ch01)
> // 0xc00003e060 (chan<- int)
> // 0xc00003e060 (<-chan int)
> //fmt.Println()
>
> func02(ch01, ch01)
> //0xc00003e060 (chan<- int)
> //0xc00003e060 (<-chan int)
>
> }
>
> func func01(f_chWriteOnly chan<- int, f_chTakeOnly <-chan int) {
> fmt.Printf("%v (%T)\n", f_chWriteOnly, f_chWriteOnly)
> fmt.Printf("%v (%T)\n", f_chTakeOnly, f_chTakeOnly)
> }
>
> func func02(f_chA chan int, f_chB chan int) {
> f_chWriteOnly := (chan<- int)(f_chA)
> f_chTakeOnly := (<-chan int)(f_chB)
> fmt.Printf("%v (%T)\n", f_chWriteOnly, f_chWriteOnly)
> fmt.Printf("%v (%T)\n", f_chTakeOnly, f_chTakeOnly)
> }
>
> ```
>
### Go 管道(channel)之panic問題:
> 管道的使用必須謹慎,以下五種狀況皆會讓程式執行發生 **panic**:
> > 1. 向裝滿元素的管道繼續寫入元素(**deadlock**)
> > 2. 向已關閉的管道繼續寫入元素(**deadlock**)
> > 3. 向無元素且未關閉的管道繼續取出元素(**deadlock**)
> > 4. 向無元素且未關閉的管道繼續取出元素(**deadlock**)
> > 5. 向已關閉的管道再次發出關閉 (重複關閉管道)(**panic: close of closed channel**)
>
> 管道可透過 ``close(管道名稱)`` 函式關閉,關閉後的管道不能再寫入元素,向無元素且已關閉的管道取值會得到該管道元素型別的預設值;利用 ``<-`` 取值時會同時回傳一邏輯值用以判斷該管道是否已關閉(**需注意:必須透過向管道取值,才能真正確認管道是否關閉**),為了安全使用管道的功能,可透過 ``len(管道名稱)`` 計算管道元素數、 ``cap(管道名稱)`` 計算管道容量,以即defer-recover...等技巧配合使用。
>
> ```go=
> package main
> import "fmt"
> func main() {
>
> var vich_Example = make(chan int, 10)
> for index, max := 0, cap(vich_Example); index < max; index++ {
> vich_Example <- index
> } //寫入前先計算長度,避免管道容量已滿再寫入導致panic
>
> close(vich_Example) //關閉管道避免,管道元素取光後產生panic(deadlock))
> for value := range vich_Example { //close 管道 + for-range解析管道可避免管道內無元素產生panic
> fmt.Printf("%v(%T)\n", value, value)
> }
>
> fmt.Printf("%v\n", len(vich_Example)) //0 取值完後管道內資料將消失
>
> if _, check := <-vich_Example; !check {
> fmt.Printf("通道已關閉\n")
> }
> //注意對管道取值檢測可得到回傳邏輯值,
> //但若邏輯值為true則表示管道元素將被取出(無法避免)
>
> // 0(int)
> // 1(int)
> // 2(int)
> // 3(int)
> // 4(int)
> // 5(int)
> // 6(int)
> // 7(int)
> // 8(int)
> // 9(int)
> // 0
> // 通道已關閉
>
> }
>
> ```
>
> ```go=
> package main
> import "fmt"
> func main() {
>
> var ifch_Example = make(chan interface{}, 10)
> //空接口型別的管道是為最簡便的使用方式
>
> ///////////////////////////////////////////////////////////
> ifch_Example <- "寫入字串"
> for index, max := 0, cap(ifch_Example)-len(ifch_Example); index < max; index++ {
> ifch_Example <- index
> } //寫入前先計算長度與容量,避免管道容量已滿再寫入導致panic
> fmt.Println()
> ///////////////////////////////////////////////////////////
>
> ///////////////////////////////////////////////////////////
> close(ifch_Example) //關閉管道避免,管道元素取光後產生panic
> var err error
> err = fc_CloseEIFChannel(ifch_Example)
> if err != nil {
> fmt.Printf("管道關閉異常 - %v\n", err)
> } //設置二次關閉通道警告,避免panic產生中斷執行
> fmt.Println()
> ///////////////////////////////////////////////////////////
>
> ///////////////////////////////////////////////////////////
> for index, max := 0, len(ifch_Example); index < max; index++ {
> fmt.Printf("%v\n", <-ifch_Example)
> } //管道關閉後仍能取值,但關閉後的管道無法再寫值
> fmt.Println()
> ///////////////////////////////////////////////////////////
>
> ///////////////////////////////////////////////////////////
> err = fc_WriteToEIFChannel(ifch_Example, 99)
> if err != nil {
> fmt.Printf("管道寫入異常 - %v\n", err)
> } //設置通道寫入警告,避免panic產生中斷執行
> fmt.Println()
> ///////////////////////////////////////////////////////////
>
> ///////////////////////////////////////////////////////////
> if fc_IsEIFChannelEmpytClose(ifch_Example) {
> fmt.Printf("管道內已無元素且已關閉\n")
> } //可對管道進行元素數檢測和關閉檢測,避免因檢測同時元素取出
> fmt.Println()
> ///////////////////////////////////////////////////////////
> //本程式碼展示如何利用空接口安全的關閉/寫入管道:
> //----------------------------------------------------
> // 管道關閉異常 - close of closed channel
> //----------------------------------------------------
> // 寫入字串
> // 0
> // 1
> // 2
> // 3
> // 4
> // 5
> // 6
> // 7
> // 8
> //----------------------------------------------------
> // 管道寫入異常 - send on closed channel
> //----------------------------------------------------
> // 管道內已無元素且已關閉
> //----------------------------------------------------
>
> }
>
> func fc_WriteToEIFChannel(channel chan interface{}, value interface{}) (err error) {
> defer func() {
> if err_recover := recover(); err_recover != nil {
> //fmt.Printf("%v(%T)\n", err_recover, err_recover)
> //管道寫入失敗,通常原因為該管道已經關閉
> err = err_recover.(error)
> }
> }()
>
> channel <- value
> return
> }
>
> func fc_CloseEIFChannel(channel chan interface{}) (err error) {
> defer func() {
> if err_recover := recover(); err_recover != nil {
> //fmt.Printf("%v(%T)\n", err_recover, err_recover)
> //管道關閉失敗,通常原因為該管道已經關閉,或者輸入資料並非管道
> err = err_recover.(error)
> }
> }()
>
> close(channel)
> return
> }
>
> func fc_IsEIFChannelEmpytClose(channel chan interface{}) bool {
> if _, check := <-channel; !check && len(channel) == 0 {
> return true
> } else {
> return false
> }
> //可對管道進行元素數檢測和關閉檢測,避免因檢測同時元素取出
> }
>
> ```
> 通道-map 應用 : https://play.golang.org/p/eXwppMgHR3
### Go 實現多核並行計算(goroutines):
>
>
>
> 各作業系統大多將程式的運作分為:「程序(process)」、「執行緒(thread)」、「服務(Background process)」,Golang並行處理機制使用 **goroutines** 是為golang中最基本的執行單元,使用 ``go`` 作為使用語法。
>
>
> 無控制使用:
> ```go=
> package main
> func main() {
>
> for index := 0; index < 10; index++ {
> go fc_Test01()
> go fc_Test02()
> print("X")
> }
> // XAABBXXXBBAXBXAXBBAAXBAXBAXB
> // XXBAABBXABAXXBBAXXBAXBAXBABXA
> // XBABXABAXXBBAXABAXXABAXBBAXX
> // XBBXAABXABAXXBABXABAXXBBAXABAX
> // 執行結果之順序與次數將無法獲得保證
> }
>
> func fc_Test01() {
> print("A")
> }
>
> func fc_Test02() {
> print("B")
> }
>
> ```
> 利用sync包(保證每個回權執行次數相等):
> ```go=
> package main
> import "sync"
>
> var syncWG sync.WaitGroup
>
> func main() {
>
> for index := 0; index < 1000; index++ {
>
> syncWG.Add(2)
>
> go fc_Test01()
> go fc_Test02()
>
> syncWG.Wait()
> print("X")
> }
>
> //ABXBAXBAXBAXBAXBAXBAXBAXBAXBAX
> //BAXABXABXABXBAXBAXBAXBAXBAXBAX
> //執行結果之次數能獲得保證,但次序仍不一定
>
> }
>
> func fc_Test01() {
> defer syncWG.Done()
> print("A")
> }
>
> func fc_Test02() {
> defer syncWG.Done()
> print("B")
>
> }
> ```
> 利用select關閉(goroutines):
> ```go=
> package main
>
> import (
> "fmt"
> "log"
> "runtime"
> "time"
> )
>
> var vbch_Done = make(chan bool)
>
> func main() {
>
> var input bool
> go func() {
> for {
> select {
> case <-vbch_Done:
> fmt.Println("vbch_Done elemt check(A) : ", len(vbch_Done))
> return
> default:
> log.Println("A Still working !")
> time.Sleep(time.Second * 2)
> }
>
> }
> }()
>
> go func() {
> for {
> select {
> case <-vbch_Done:
> fmt.Println("vbch_Done elemt check(B) : ", len(vbch_Done))
> return
> default:
> log.Println("B Still working !")
> time.Sleep(time.Second * 2)
> }
>
> }
> }()
>
> go func() {
> for {
> select {
> case <-vbch_Done:
> fmt.Println("vbch_Done elemt check(C) : ", len(vbch_Done))
> return
> default:
> log.Println("C Still working !")
> time.Sleep(time.Second * 2)
> }
>
> }
> }()
>
> fmt.Printf("input anything to stop it,number of goroutines:%v\n", runtime.NumGoroutine())
> vi_MaxNumGoroutine := runtime.NumGoroutine() //紀錄最高Goroutine數量
> for {
> fmt.Scanf("%v", &input)
> fmt.Printf("Passed : %v , number of goroutines:%v\n", input, runtime.NumGoroutine())
> if vi_MaxNumGoroutine-runtime.NumGoroutine() < (vi_MaxNumGoroutine - 1) { //偵測Goroutine數量是否低於已將 A/B/C關閉數
> vbch_Done <- true
> } else {
> fmt.Println("All Goroutines stopped")
> }
>
> }
>
> // input anything to stop it:
> // 2020/02/10 21:47:58 A Still working !
> // 2020/02/10 21:47:58 B Still working !
> // 2020/02/10 21:47:58 C Still working !
> // Scan任一輸入後會將其中一個goroutines中斷
>
> }
>
> ```
### Go 驗證多核並行計算(goroutines):
> 不要通过共享内存来通信, 而要通过通信来共享内存” --- 出自《 Effective Go
> golang 的設計讓 concurrency 非常容易。
> https://blog.golang.org/concurrency-is-not-parallelism
>
>
> 驗證多核心運算(切片加總計算):
> ```go=
> package main
>
> import (
> "log"
> "runtime"
> "time"
> )
>
> func StartTimer() time.Time {
> return time.Now()
> }
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var visl_Test = []int{}
>
> for index := 0; index < 20000000; index++ {
> visl_Test = append(visl_Test, index)
> }
>
> var vf_Time1 = StartTimer()
> println(fc_SLSUM_N(visl_Test))
> log.Printf("\n耗費:%.4f ms", SubTimer(vf_Time1))
> // 199999990000000
> // 2019/09/26 15:57:33
> // 耗費:17.0010 ms
>
> time.Sleep(time.Millisecond * 500)
>
> var vf_Time2 = StartTimer()
> println(fc_SLSUM_MaxParallel(visl_Test))
> log.Printf("\n耗費:%.4f ms", SubTimer(vf_Time2))
> // 199999990000000
> // 2019/09/26 15:57:34
> // 耗費:10.0006 ms
>
> }
>
> func fc_SLSUM_N(visl_numbers []int) int {
> var vi_total int
> for _, vi_number := range visl_numbers {
> vi_total += vi_number
> }
> return vi_total
> }
>
> func fc_SLSUM_MaxParallel(visl_numbers []int) int {
> var vi_nCPU = runtime.NumCPU()
>
> var vi_nNum = len(visl_numbers)
>
> vich_Channel := make(chan int)
> for i := 0; i < vi_nCPU; i++ {
> vi_L := i * vi_nNum / vi_nCPU
> vi_R := (i + 1) * vi_nNum / vi_nCPU
> go func() {
> var vi_cache int
> for _, vi_number := range visl_numbers[vi_L:vi_R] {
> vi_cache += vi_number
> }
> vich_Channel <- vi_cache
> }()
> }
>
> var vi_Sum int
> for i := 0; i < vi_nCPU; i++ {
> vi_Sum += <-vich_Channel
> }
> return vi_Sum
> }
>
> ```
>
> 驗證多核心運算(取值運算):
>
> ```go=
> package main
>
> import (
> "fmt"
> "log"
> "strconv"
> "time"
> )
>
> func StartTimer() time.Time {
> return time.Now()
> }
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var vsar_Strings [1000]string
> for index := 0; index < 1000; index++ {
> vsar_Strings[index] = "元素" + strconv.FormatInt(int64(index), 10)
> println("元素" + strconv.FormatInt(int64(index), 10))
> }
>
> var vf_Time1 = StartTimer()
>
> for index := 0; index < len(vsar_Strings); index++ {
> println(vsar_Strings[index])
> }
>
> log.Printf("\n耗費:%.4f ms", SubTimer(vf_Time1))
> fmt.Println("[結束]")
>
> // 2019/09/26 18:29:29
> // 耗費:457.0261 ms
> // [結束]
>
> }
>
> ```
>
> ```go=
> package main
>
> import (
> "fmt"
> "log"
> "strconv"
> "time"
> )
>
> func StartTimer() time.Time {
> return time.Now()
> }
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var vsch_Strings = make(chan string, 1000)
> for index := 1; index <= 1000; index++ {
> vsch_Strings <- "元素" + strconv.FormatInt(int64(index), 10)
> println("元素" + strconv.FormatInt(int64(index), 10))
> }
>
> var vf_Time1 = StartTimer()
> go func() {
> defer func() {
> println("執行A中止!!")
> }()
> for {
> println("執行A", <-vsch_Strings)
> }
>
> }()
>
> go func() {
> defer func() {
> println("執行B中止!!")
> }()
> for {
> println("執行B", <-vsch_Strings)
> }
> }()
>
> go func() {
> for {
> if len(vsch_Strings) == 0 {
> log.Printf("\n耗費:%.4f ms", SubTimer(vf_Time1))
> fmt.Println("[結束]")
> break
> }
> }
> }()
>
> // 2019/09/26 18:19:19
> // 耗費:113.0098 ms
> // [結束]
>
> var vs_stop string
> fmt.Scanln(&vs_stop) //Scanln防止程式提前中斷
> }
>
> ```
>
> ```go=
> package main
>
> import (
> "fmt"
> "log"
> "strconv"
> "time"
> )
>
> func StartTimer() time.Time {
> return time.Now()
> }
> func SubTimer(input time.Time) float64 {
> return time.Now().Sub(input).Seconds() * 1000
> }
>
> func main() {
>
> var vsch_Strings = make(chan string, 1000)
> for index := 1; index <= 1000; index++ {
> vsch_Strings <- "元素" + strconv.FormatInt(int64(index), 10)
> println("元素" + strconv.FormatInt(int64(index), 10))
> }
>
> var vf_Time1 = StartTimer()
> go func() {
> defer func() {
> println("執行A中止!!")
> }()
> for {
> println("執行A", <-vsch_Strings)
> }
>
> }()
>
> go func() {
> defer func() {
> println("執行B中止!!")
> }()
> for {
> println("執行B", <-vsch_Strings)
> }
> }()
>
> go func() {
> defer func() {
> println("執行C中止!!")
> }()
> for {
> println("執行C", <-vsch_Strings)
> }
>
> }()
>
> go func() {
> defer func() {
> println("執行D中止!!")
> }()
> for {
> println("執行D", <-vsch_Strings)
> }
> }()
>
> go func() {
> for {
> if len(vsch_Strings) == 0 {
> log.Printf("\n耗費:%.4f ms", SubTimer(vf_Time1))
> fmt.Println("[結束]")
> break
> }
> }
> }()
>
> // 2019/09/26 18:53:33
> // 耗費:272.0155 ms
> // [結束
>
> var vs_stop string
> fmt.Scanln(&vs_stop) //Scanln防止程式提前中斷
> }
>
> ```