# Day18 無言的省略號(...),以及目錄架Go 這邊的無言,不是說Go的用法令人無言啦 ~~雖然有時候還真的有點無言,例如各種`if err != nil`的時候~~ ## 三個點的省略符號:刪節號`... ` 除了人們會用`...`來代表**我很無言**之外, 在程式中也常會看到Golang很無言的... 常見的俗名或稱呼方式為: 1. 點點點 (Dot Dot Dot) 2. 三個點 (Three Dot、Truple Dot) 3. 刪節號 (Ellipsis) ... ### 在Golang中,刪節號 `...` 共有四種用法: ## 用法1 - 省略陣列長度 在之前 [**Day09**](https://ithelp.ithome.com.tw/articles/10237400) 宣告**Array**的時候,我們也用過省略符號來省略長度。 省略長度是要編譯器幫我們計算Array陣列長度、自動將長度填入 **而不是沒有長度的Slice**。 https://play.golang.org/p/3G9tKGdOy1o ``` func main() { var a = [...]int{1, 2, 3, 7, 8, 9} fmt.Println(len(a)) fmt.Println(a) } /* 6 [1 2 3 7 8 9] */ ``` ## 用法2 - 可變參數函式 那麼這個`刪節點、省略符號`用在函式中,就稱作 **可變函式(參數長度可變) Variadic Functions** 意思即為,可以傳入多個參數(0~N個),不傳入也行。 長度未知、在不知道參數到底有幾個的時候,我們可以透過這個方法來解決,但需為同樣型別。其實`...` 就有點像`[] Slice` 的意思。 ### 小坑注意 `...`在一個func參數中只能出現一次,而且是放在最後的參數位置上 ``` func test1(...int) { } // 省略符號只能放在最後的位置上 func test2(int, ...string) { } // 這個會報錯 Can only use '...' as final argument in list func test3(... int, ... string) { } ``` > > 先滿足必要的參數,再來填入剩下的剩餘參數 > > > 換角度思考一下,如果一開始在第一個元素上放省略符號 > 我大Golang編譯器要怎麼知道此人到底想省略幾個元素? https://play.golang.org/p/KqmPFdQXTHV ```go func main() { s1 := sum() fmt.Println(s1) s2 := sum(1, 5, 9) fmt.Println(s2) } func sum(nums ...int) int { var total int for _, num := range nums { total += num } return total } /* 0 15 */ ``` 在這個地方,省略長度的int `nums`,**像極了Slice** ![像極了Slice](https://i.imgur.com/LA8Nmzx.png) 根本就是Slice ## 用法3 - 開箱、解包、解壓縮 Unpacking 在不同的地方上面可以代表不同的意涵, 省略符號 `...` 也可以使用在很暴力殘忍的地方上,例如 `將Slice 剝皮`。 水能載舟亦能覆舟,才剛把他弄成Slice,現在又要將他剝皮。 ```go func sumUnpacking(nums ...int) int { // 傳入int但不曉得參數長度為何 var total int for _, num := range nums { total += num } return total } func main() { slice := []int{2, 3, 5} sum1 := sumUnpacking(slice...) // 把slice 解開、剝皮後傳入,同下 fmt.Println(sum1) } /* result: 10 */ ``` > 那到底什麼時候是`Slice`、什麼時候是`省略長度`、什麼時候會被`剝皮`? 以下整理了三種不同的方式來比較、探討Slice與 功能皆為實現一群數字的`加總` ### Sum 一群數字的加總 https://play.golang.org/p/PKdWcjr_RUe ```go func main() { slice := []int{2, 3, 5} sum1 := sumUnpacking(slice...) // 把slice 解開、剝皮後傳入,同下 fmt.Println(sum1) sum2 := sumUnpacking(2, 3, 5) // 可變參數函式 fmt.Println(sum2) sum3 := sumSlice(slice) // 不曉得int長度,也可以直接包成一個slice型別來傳遞 fmt.Println(sum3) } func sumUnpacking(nums ...int) int { // 傳入int但不曉得參數長度為何 var total int for _, num := range nums { total += num } return total } func sumSlice(nums []int) int { // 傳入slice var total int for _, num := range nums { total += num } return total } /* result: 10 10 10 */ ``` ### Append 一群數字的合併 要怎麼使用 `Append` 來合併兩個陣列?   這邊整理了三種合併一群數字的方法。 https://play.golang.org/p/Q138HLRIuFF ```go func main() { slice1 := []int{1, 2, 3, 4, 5} slice2 := []int{6, 7, 8} // 將兩 Slice 合併(append) 的方法 - 1 a1 := append(slice1, slice2[0], slice2[1], slice2[2]) // append用法,每個參數只能附上一個int fmt.Println(a1) // 將兩 Slice 合併(append) 的方法 - 2 a2 := slice1 for _, num := range slice2 { // 如方法1,只是這次透過for迴圈來迭代完成 a2 = append(a2, num) } fmt.Println(a2) // 將兩 Slice 合併(append) 的方法 - 3 a3 := append(slice1, slice2...) // 直接將slice2 剝皮解壓縮後(Unpacking)再執行append,取代上面使用For迭代的方法 fmt.Println(a3) } /* result: [1 2 3 4 5 6 7 8] [1 2 3 4 5 6 7 8] [1 2 3 4 5 6 7 8] */ ``` ## 用法4 - go test 跑測試 這不是程式內的用法, 而是在編譯、跑測試時才會用到執行的指令 $ go test ./... 將自動運行所有這目錄底下的Package的測試程式, 當然要將所有的測試檔案 `_test.go` file 先寫好,包括預設輸入及預期輸出。 --- # Golang中的目錄架構 **GOROOT** 放的是安裝的go語言、官方內建的函式庫 例如寫程式時經常會用到的"fmt"就放在這底下 import "fmt" **GOPATH** 放的是別人開發的第三方套件、以及自己的程式碼 import "github.com...(別人的repository)" 照慣例通常會把自己寫的專案及程式放入**GOPATH**底下的**src(source)**資料夾裡, 如果有用Github會再多一層 github.com 及 account。 > 以上的分法都通常是在較久遠的年代,透過`glide`或`dep`來管理。 > 自從`Go 1.11` 推出了`gomod`後,就不一定要把專案放到GOPATH底下,也可以正常運行 > 但這邊還是偏向使用`GOPATH`及`glide套件管理工具` ## GOPATH目錄架構 使用者目錄 (C:\Users\USER 或 ~/)  - **Go** (GOPATH) 如果底下沒有bin/pkg/src目錄,別擔心,自己新增一個   - bin  (放編譯過後的可執行檔案 二進制檔案)   - pkg  (放編譯過後的library)   - **src**  (放原始碼 Source Code)       - **github.com**         - **Jack** (使用者名稱)           - **HelloWorldProject** (專案名稱)             - **main.go** (主程式)         - Tom           - MyCoolProject             - core  (函式庫名稱)               - core.go               - xxxx.go             - libs  (函式庫名稱)               - lib.go               - oooo.go             - main.go       - gitlab.com          - ... ## 專案?包?套件? **專案Project、包Package** 都是P開頭,傻傻分不清? 套件是別人寫好的Project或Package,只是我們把他拿來使用。 看到這邊,是時候來回顧這篇文章了 [**寫程式之前,GO這些很重要**](https://ithelp.ithome.com.tw/articles/10235523)