Introducing Go - 筆記

LOGO.jpg



簡述



這本書是一本入門的書,在這裡我會寫下屬於我自已的筆記,希望對你也有用。

(PS: 我是一個己經會寫程式的人,英文普通)

紀錄:




  • 基本語法

  • 其他語言不同之處

  • 閱讀時會碰觸到的英文



觀念



Go 是一種強型別的語言。

一般來說盡量使用x := 5這種方式進行宣告

通常變數命名的風格用小寫駝峰

在Go中,參數傳遞的方式總是以copied(傳值)的方式

全域變數只能用var name type的型式宣告,無法使用x := 5的方式

Nan -> Not a Number (i.g. 0/0, +∞, -∞)



指令



查功能 go doc [package] [function]

->如果拿到 "go doc command not found" 嚐試這個指令安裝說明文件

-> go get golang.org/x/tools/cmd/godoc



語法



Go不支援三元運算子?:

字串除了"text"的形式,也可以是`text`

+來連結字串

_來接收用不到的回傳值



variable



var a = 5
a := 5
var (
a = 5
b = 10
c = 15
)


for



// 1.只有條件
for 條件 {
...
}

// 2.含設初值與迭代
for 設初值; 條件; 迭代 {
...
}

// 3. 無窮迴圈
for 設初值; 條件; 迭代 {
...
}


if



if 條件 {
...
}

if 條件 {
...
} else if 條件 {
...
}

if 條件 {
...
} else {
...
}

if 賦初值; 條件 {
...
}

switch var {
case val: ...
case val: ...
case val: ...
case val: ...
case val: ...
case val: ...
default: ...
}



array



陣列最後一個元素要有,



var x [5]int
x[4] = 100 // [0, 0, 0, 0, 100]
// -----
x := [5]int { // [1,2,3,4,5]
1,2,3,4,5,
}
// -----
x := [5]int { // [1,2,3,0,0]
1,2,3,
}



slices



是陣列的一部份(所以其實他是某個陣列的子集),其長度可變



var x[]int // slices長度為0
// -----
x := make([]int, 5) // slices長度為5
// -----
x := make([]int, 5, 10) // slices長度為5,其父array長度為10
// -----
arr := [5]int{1,2,3,4} // 這是一個[1,2,3,4,0]的陣列
x := arr[0:3] // 這是一個[1,2,3]的切片(Slices)


append


var_slice = append(var_slice, var1, var2 ..)

append是slices之下的方法,只能對slices使用,無法對array使用。

這個方法會在slices的末端加上內容。(若其父array的長度不夠會建立另一個slices並把原本的內容copy過去,再加上新的內容)



slice1 := []int{1,2,3,4} // 這是一個[1,2,3,4]的slices
slice2 := apend(slice1, 4, 5) // [1,2,3,4,4,5]


copy


copy(var_slice_dst, var_slice_src) copy是slices之下的方法,只能對slices使用,無法對array使用。

這個方法會把src復製到dst中(只會復製長度足夠的部份)。



slice1 := []int{1,2,3,4} // [1,2,3,4] slices
slice2 := make([]int, 2) // [0,0] slices
copy(slice2, slice1)
// slice2 -> [1,2]


maps



關聯陣列。



var x map[string]int{} //x是一個string對應int的關聯陣列。
// -----
x := make(map[string]int)
x["a"] = 1
x["b"] = 2
x["c"] = 3
// map[a:1 b:2 c:3]
// ------
x := map[string]int {
"a": 1,
"b": 2,
"c": 3,
}


maps返回兩個值: 取回來的值, 是否找到該值



// 承上 x => map[a:1 b:2 c:3]
val, isOk = x["a"] // 1, true
val, isOk = x["d"] // 0, false



2維的maps



// x是一個string對應map的關聯陣列
// x是一個string對應map ( string對應string關聯陣列 ) 的關聯陣列
x := map[string]map[string]string {
"a": map[string]string {
"sub-a1": "a1",
"sub-a2": "a2",
},
"b": map[string]string {
"sub-b1": "b1",
"sub-b2": "b2",
},
}
// map[a:map[sub-a1:a1 sub-a2:a2] b:map[sub-b1:b1 sub-b2:b2]]


函式



func foo(param) return val {
...
}

// 回傳多個值
func foo() (int, int) {
...
return 1, 2
}

// 回傳有名字的變數
func foo() (r int) {
r = 1
return // 因為r是1, 所以會回傳1
}


variadic 可變參數 (...)



... 被用於函式的最後一個參數,他可以把多於的參數都放入最後一個參數中。



func add(x int, args ...int) int {
total := 0
for _, v := range args {
total += v
}
return total
}
func main() {
fmt.Println(add(1, 2, 3, 4, 5)) // 14
}


也可以利用...將陣列展開並放到可變參數中



func add(x int, args ...int) int {
total := 0
for _, v := range args {
total += v
}
return total
}
func main() {
xs := []int{1,2,3,4,5}
fmt.Println(add(1,xs...)) // 14
fmt.Println(add(xs...)) // error "not enough arguments in call to add"
}


內建函式



range



range 被用在 for 中 用來迭代array, slices, maps,他會返回 index, value 或 key, value



作用域scope



像是javascript,從裡往外找。



閉包closure



func countFactory() func()(int) {
i := 0
return func() (int) {
i++
return i
}
}
func main() {
countFunc := countFactory()
fmt.Println(countFunc()) // 1
fmt.Println(countFunc()) // 2
countFunc2 := countFactory()
fmt.Println(countFunc2()) // 1
fmt.Println(countFunc()) // 3
fmt.Println(countFunc2()) // 2
fmt.Println(countFunc2()) // 3
}


defer (推遲)



用來將某個語句推遲到該函式的return前執行。(常用在file) 就算發生panic,defer還是會執行!



f, _ := os.Open(filename)
defer f.Close() // 這樣就不用擔心忘記close了,也不會讓file的code到處都是



  • defer會記住當下的狀態

  • defer會先進後出



panic 和 recover



有點像是php的throw Exception, try catch,但是用法上有些區別。

首先發生panic,然後可以利用recover來取得panic傳入的錯誤訊息。



// panic
panic("PANIC") // 從這裡就會跳出Error
fmt.Println("123") // 不會被執行,因為前一行panic了
// -----
// 錯誤的使用
panic("Error Msg")
str := recover() // 不會執行
fmt.Println(str) // 不會執行


一般來說,我們會想知道panic跳出什麼錯誤,但是panic會中斷函式,所以我們會利用defer的特性,抓到panic跳出的錯誤訊息。



defer func() {
str := recover()
fmt.Println(str) // 印出panic
}()
panic("panic")


指標 *, &



基本同C語言 * -> 取值, & -> 取址



new



宣告一個類型的變數並回傳其地址 xPtr := new(int)



structs 結構



基本同C語言



type Point struct {
x int
y int
}
x := Point{1, 2} // 照順序賦值
fmt.Println(x) // {1 2}
fmt.Println(x.x) // 1
fmt.Println(x.y) // 2
// ------
x := Point{y:1, x:2} // 照key賦值
fmt.Println(x) // {2 1}




methods for struct 結構的方法



一般來說我們會新增一個方法,並接收一個struct type的參數,但是在Go中可以為struct 新增方法,讓使用上更直覺。



type Point struct {
x int
y int
}
func (p *Point) plusPoint() int {
return p.x + p.y
}
func main() {
x := Point{1, 2}
fmt.Println(x.plusPoint()) // 3
}


embedded types 嵌套結構



結構中可以含有另一個結構,用例子的方式來解釋。

先有一個結構->人類,有一個說出自己性別的方法TalkSex()。



type Human struct {
Sex bool
}
func (p *Human) TalkSex() {
if p.Sex {
fmt.Println("I am male.")
} else {
fmt.Println("I am female.")
}
}

func main() {
x := Human{Sex: true}
fmt.Println(x) // {true}
x.TalkSex() // I am male.
}


現在有一個新的結構Person,Person結構中包含了Human。

// 結構上成立,但語意不成立 (我們會說一個人含有名字這個屬性,但不會說一個人含有人類的屬性)



// "一個人"含有"人類"的屬性
type Human struct {
Sex bool
}
func (p *Human) TalkSex() {
if p.Sex {
fmt.Println("I am male.")
} else {
fmt.Println("I am female.")
}
}
type Person struct {
Human Human
Name string
}

func main() {
x := Person{Human{Sex: true}, "jack"}
fmt.Println(x) // {{true} jack}
x.Human.TalkSex() // I am male.
}


一般來說,我們會說Person是個Human( Person is-a Human) // 一個人 是個 人類

但是在上述範列中變成"一個人 含有 人類",所以當我們需要is-a這種關系的時候會這樣使用



// "一個人" 是 "人類"
type Human struct {
Sex bool
}
func (p *Human) TalkSex() {
if p.Sex {
fmt.Println("I am male.")
} else {
fmt.Println("I am female.")
}
}
type Person struct {
Human // 注意這裡,這裡說明了 Person is-a Human 的關系
Name string
}

func main() {
x := Person{Human{Sex: true}, "jack"}
fmt.Println(x) // {{true} jack}
x.TalkSex() // I am male.
x.Human.TalkSex() // I am male. // 也可以用這樣用(不常用)
}


區別 含有某個屬性是屬於(is-a)很有必要,這會大大的影響可讀性,與直覺上函式的使用方法。




  • 是否使用 embedded types 取決於你要用is-a(用),還是has(不該用)的關系。



interface



interface定義方法集合(method set)



type Shape interface { // 裡面應只有一堆方法
calArea() float64
}
// 任何struct只要有calArea() float64,就算是Shape


只要某個struct的方法對應的上,那就算是該interface。

(把interface想像成濾網,只要struct的方法與interface一模一樣,就通過了)



type Rectangle struct {
x1, y2, x2, y2 float64
}
func (r *Rectangle) calArea() float64 {
l := distance(r.x1, r.y1, r.x1, r.y2)
w := distance(r.x1, r.y1, r.x2, r.y1)
return l * w
}
// 因為Rectangle這個結構有calArea() float64,所以算也屬於Shape這interface。

type Circle struct {
x1, y2, x2, y2 float64
}
func (c *Circle) calArea() float64 {
return math.Pi * c.r * c.r
}
// 因為Circle這個結構有calArea() float64,所以算也屬於Shape這interface。


interface跟struct一樣,可以被當成參數傳遞。

這讓不同的struct,可以因為interface的關系被傳入到同一個函式中。



func sumArea(shapes ...Shape) float64 {
area := 0.0
for _, s := range shapes {
// 只能使用s.calArea(),不能使用原struct的屬性。
// 因為你代入的是Shape這個interface,而該interface只有calArea()這個方法。
area += s.calArea()
}
return area
}


interface也可以被放到struct中。



type ALotShape struct {
shapes []Shape
}

// 若是我們為這個struct加上 calArea() float64,那他也算是Shape這interface。
func (a *ALotShape) calArea() float64 {
area := 0.0
for _, s := range a.shapes {
area += s.calArea()
}
return area
}


所以




  • 透過為struct增加method來符合某個interface

  • 透過interface來接收多個不同種類卻有相同方法的struct

  • interface關注的是方法,這使在開發程式的時候不需要去關注struct的細節。

  • interface與struct只在方法上偶合,所以更動struct的時候不必在意結構與繼承的關系。



語法糖



x := 5, var x = 5 -> Go會自動判斷賦值的型別,並宣告給x。

arr[0:] 等同於arr[0:len(arr)]

arr[:5]等同於arr[0:5]

arr[:]等同於arr[0:len(arr)]



英文單字




























































































































































































































































































































































































































































英文 中文
distributed 分散式
domains 領域
simplicity 簡單
low barrier 低門檻
abundance 豐富
geared towards 面向
exhaustive 全面詳盡的
trackle "something" 解決 ..
rudimentary 初級的
concurrency 並發
preceding chapters 上一章
significantly 顯著地
conventions 公約/約定
typographical 印刷的
literally 字面上地
determined 決定的
advanced features 高級功能
robust 健全的
prior 先/事先的
concise 簡潔的/簡明扼要的
consulting 諮詢
intimidating 可怕的
mechanism 機制
altering 改變
corresponding 相應的
interact 相互影響
identical 完全相同的
subsequent 隨後的
pedantic 迂腐的
no tolerance 無容忍
parentheses 括號
curly braces 大括號
terminology 術語
deliberately 故意地
nevertheless 儘管如此地
categorize 分類 ..
grasp 把握 ..
perspectives 觀點
philosophers 哲學家
distinction 區別
cumbersome 麻煩的
decimal 十進制
representation 表示
inexact 不準確的
occasionally 偶爾地
arithmetic 算術
precisions 精度
respectively 分別地
remainder 餘數
sufficient to 足以去 ..
sophisticated 複雜的
individual 各別的
backtick 反引號 `
concatenation 串聯,串起
formulas 公式
involve 包括 ..
manipulation
transformation 轉型
foundation 基礎
algebra 代數
subtle 微妙的
dendency to 傾向 ..
theorem 定理
infer 推斷
informal 非正式的
phenomenon 現像
semantically 語義上地
idiomatic 慣用語的
worthwhile 值得的
pursuit 追求
phrase 短語
specification 規範
lexically 詞彙上地
essentically 本質上地
evaluate 評估
divisible 可約分的
slices 切片
segment 部分/片段
capacity 容量
subroutine 子程序
procedure 程序
independent section 獨立的部份
callee 被調用者
variadic 可變參數
ellipsis 省略
precisely 恰恰地/剛剛好地
persists 持續存在
recursion 遞迴
tempt to do 情不自禁的去做 ..
hence 因此
rarely 很少地
collapse fields 折疊字段
intuitively 直覺地
accident 事故
commonplace 常見的/普通的
accidental 偶然的
explicit 明確的
poligons 多邊形
taxonomy 分類
incidental details 附加細節
embodied 體現的
utilize 利用
overlapping 重疊的/重覆的
succinct 簡潔的
from scratch 從頭開始
cryptography 密碼學
efficient 高效的
underneath 在...之下


留言

這個網誌中的熱門文章

成人剪舌繫帶聽過嗎?我剪了!!

Scp - ssh 的遠端檔案傳輸指令

睡覺使你更有效率