iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
3
Software Development

Go繁不及備載系列 第 18

# Day18 無言的省略號(...),以及目錄架Go

  • 分享至 

  • xImage
  •  

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

這邊的無言,不是說Go的用法令人無言啦
雖然有時候還真的有點無言,例如各種if err != nil的時候

三個點的省略符號:刪節號...

除了人們會用...來代表我很無言之外,
在程式中也常會看到Golang很無言的...
常見的俗名或稱呼方式為:

  1. 點點點 (Dot Dot Dot)
  2. 三個點 (Three Dot、Truple Dot)
  3. 刪節號 (Ellipsis)
    ...

在Golang中,刪節號 ... 共有四種用法:

用法1 - 省略陣列長度

在之前 Day09 宣告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

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
根本就是Slice

用法3 - 開箱、解包、解壓縮 Unpacking

在不同的地方上面可以代表不同的意涵,
省略符號 ... 也可以使用在很暴力殘忍的地方上,例如 將Slice 剝皮
水能載舟亦能覆舟,才剛把他弄成Slice,現在又要將他剝皮。

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

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

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。

以上的分法都通常是在較久遠的年代,透過glidedep來管理。
自從Go 1.11 推出了gomod後,就不一定要把專案放到GOPATH底下,也可以正常運行
但這邊還是偏向使用GOPATHglide套件管理工具

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這些很重要


上一篇
# Day17 Go-拖延症候群 (defer)
下一篇
# Day19 Go-glide 套件管理工具
系列文
Go繁不及備載35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
西撒
iT邦新手 5 級 ‧ 2020-10-28 23:54:15

gopath src 建議不要在使用
請用 go module

配合社群規範展開 project layout
看到 專案有 src 的, 我都覺得來亂得

https://github.com/golang-standards/project-layout

https://juejin.im/post/6844903565496958990
類似 ddd 的概念, 來分配目錄結構

雷N iT邦研究生 1 級 ‧ 2020-10-31 11:06:57 檢舉

部門自己定義好就行了吧?
DDD也能協助用來協助切割服務邊界

我要留言

立即登入留言