---
# System prepended metadata

title: Day18 無言的省略號(...)，以及目錄架Go

---

# 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)

