DukeHuangWp
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
###### 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防止程式提前中斷 > } > > ```

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully