- 公有 package 的相關資料都可以在這查詢 pkg.go.dev
Hello World!
// package name 一個資料夾底下都叫同一個 package
package main
// import 引用 相依套件
import "fmt"
// 文件主要輸出 都以 main() 為出口
func main() {
fmt.Println("Hello World!")
}
// $ go run main.go
// > Hello World!
基本類型 DefalutTypes
- Boolean - true / false
- Numeric - byte / rune / (u)int / uintptr/ (u)int8 / (u)int16 / (u)int32 / (u)int64 / float32 / float64
- String - string
- Derived - Pointer / Array / Structure / Union / Function / Slice / Interface / Map / Channel
變量 Variables
// 定義 value1 和 value2 都類型是 type
var value1, value2 type
// 定義 valueName 是 value
var valueName type = value
// 同時定義多個
var v1, v2, v3 = a, b, c
// 更簡化 自動導入相對應的類型
v1, v2, v3 := a, b, c
常量 Constants
const key type = value
// 同時定義多個
const akey, bkey, ckey = aval, bval, cval
// 也可用在列舉 Examle 性別
const Unknown, Female, Male = 0, 1, 2
算符 Operators
幾乎多數的運算寫法都大同小異 可跳過
- 算數
- 關係
- 邏輯
算數
兩個數字舉例 20 與 2
運算符 | 描述 | 結果 |
---|---|---|
+ | 加 | 22 |
- | 減 | 18 |
* | 乘 | 40 |
/ | 除 | 10 |
% | 餘 | 0 |
++ | 自加 | 21 |
-- | 自減 | 19 |
關係
兩個數字舉例 20 與 2
運算符 | 描述 | 結果 |
---|---|---|
== | 相等 | False |
!= | 不相等 | True |
< | 小於 | False |
> | 大於 | True |
<= | 小於等於 | False |
>= | 大於等於 | True |
邏輯
兩個布林舉例 true 與 false
運算符 | 描述 | 結果 |
---|---|---|
&& | 相同 | False |
|| | 或著 | True |
! | 反轉 | False |
條件 Decision
- if…else - 最標準的判斷
- switch - 依序滿足條件及跳出程序,需要執行後續的case添加
fallthrough
- select - 類似 switch 但 隨機滿足條件則執行
if…else
example := true
// 以 JS相比 省略 ()
if example == true {
// func() ....
} else {
// func() ....
}
switch
default 就有 break,需要執行後續的case添加fallthrough
switch var1 {
case val1:
...
case val2:
...
// fallthrough
default:
...
}
select
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
for i := 0; i < 1; i++ {
select {
case msg1 := <-c1:
fmt.Println("receivedcase1", msg1)
case msg1 := <-c1:
fmt.Println("receivedcase2", msg1)
}
}
}
// 可以測試看看偶而會出現不同的結果
循環 Loop
// 標準
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
// while 寫法
sum := 0
for sum <= 10 {
sum += i
}
fmt.Println(sum)
// ForEach 寫法
strings := []string{"google", "runoob"}
for i, s := range strings {
fmt.Println(i, s)
}
// loops 無限
for {
fmt.Println("無止境")
}
函式 Function
/*
私域 Function - func 需要小寫開頭
func functionName(x, y type) type {
return x
}
公域 Fuction - func 需要大寫開頭
func FunctionName(x, y type) type {
return x
}
*/
// 範例1:可多項 type回傳
func swap(x string, y string) (string, string) {
return y, x
}
// 範例2:閉包
func getSequence() func() int {
i:=0
fmt.Println("第一次使用會先執行到此行,註冊成變數")
return func() int {
i+=1
fmt.Println("執行 Function")
return i
}
}
func main() {
nextNum := getSequence()
fmt.Println(nextNum())
// 1
fmt.Println(nextNum())
// 2
fmt.Println(nextNum())
// 3
}
// 範例3:Struct Func
// 這方式極常用,請熟讀領會。
type Circle struct {
radius float64
}
func (c Circle) getArea() float64 {
return 3.14 * c.radius * c.radius
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圓面積 = ", c1.getArea())
// 314
}
// 相似 ES6 Class function
// class Circle {
// constructor(radius){
// this.radius = radius
// }
// getArea(){
// return 3.14 * this.radius * this.radius
// }
// }
// var c1 = new Circle(10.00)
// console.log(c1.getArea())
// 範例4:遞歸 Function
func recursion() {
recursion() /* 使用自身邏輯在重新判斷,通常會含有判斷 */
}
func main() {
recursion()
}
字串 String
- strings pkg
- 在 golang 中字串都是一種切片 (slices)
// 將 []string{} 字串合併
examples := []string{"Hello", "world!"}
fmt.Println(strings.Join(examples, " "))
數組 Array
Array 用來定義涵蓋範圍且範圍是固定。
// name := [size]type(val)
// {} 的數量 <= []的數字
// [] 忽略數字 = 宣告時 {}的數量
// 標準
arrExample1 := [3]string{"one", "two" , "three"}
// 不確定 {} 的數量,但其實也是個註記,因為也不能擴充, example: arrExample2[3] 噴錯
arrExample2 := [...]string{"one", "two", "three"}
// 訪問數字 example: two
fmt.Println(arrExample1[1])
// 進階
// 多維數組
// 標準使用
multidiArrExample1 := [2][3]int{
{0, 1, 2, 3} ,
{4, 5, 6, 7} ,
}
// 訪問數字 example: 2
fmt.Println(multidiArrExample1[0][2])
指針 Pointer
指定記憶體的位置,變數是綽號的話,指針變數的專屬ID
var a int = 1 /* a 值為 1 */
add := &a /* add 為 a 記憶體位置(指針) */
fmt.Printf(*add) /* 透過 add 記憶體位置(指針) 得知 值為 1*/
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
fmt.Printf("交换前 a 值 : %d\n", a )
fmt.Printf("交换前 b 值 : %d\n", b )
swap(&a, &b);
fmt.Printf("交换後 a 值 : %d\n", a )
fmt.Printf("交换後 b 值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 將 y 賦值給 x */
*y = temp /* 將 temp 賦值給 y */
}
// 交换前 a 值 : 100
// 交换前 b 值 : 200
// 交換後 a 值 : 200
// 交换後 b 值 : 100
理解上方的做法後 進階簡潔
func swap(x *int, y *int){
*x, *y = *y, *x
}
再再簡潔
func main() {
var a int = 100
var b int= 200
a, b = b, a /* 交換 */
}
結構 Struct
自定義type,非常常使用,類似 TS 的 type,用法像 Object 用 . 的方式訪問
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 可忽略一些定義
Book := Books{title: "LearnForGo", author: "hifounder"}
fmt.Println(Book)
}
Struct 搭配 Pointer
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
Book := Books{ title: "LearnForGo", author: "hifounder", subject:"介紹",book_id: 00001 }
printBook(&Book) /* 給予Book 記憶體位置 */
}
func printBook( book *Books ) { /* 透過傳入的 book 記憶體位置 理解值 */
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}
切片 Slice
基本上是處理Array
// example
arr := [3]string{"one", "two" , "three"}
// Array 指的是 {"one", "two" , "three"} 這些值
// Slice 指的是 arr[] 後方 [] 這個的動作
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
func main() {
var mySlice1 [10]int
printSlice(mySlice1[:])
mySlice2 := make([]int, 10)
printSlice(mySlice2)
mySlice3 := [10]int{}
printSlice(mySlice3[:])
// 皆是 => len=10 cap=10 slice=[0 0 0 0 0 0 0 0 0 0]
}
# 定義切片
func main() {
mySlice1 := make([]int, 8, 10)
mySlice1[2] = 1
mySlice1[3] = 10
mySlice1[5] = 20
printSlice(mySlice1)
// len=8 cap=10 slice=[0 0 1 10 0 20 0 0]
printSlice(mySlice1[:3])
// len=3 cap=10 slice=[0 0 1]
printSlice(mySlice1[3:])
// len=5 cap=7 slice=[10 0 20 0 0]
// len Array長度 與 起始位置
// cap 是 指定容量
// [注意!] error: mySlice1[9] = 50
// append()
mySlice1 = append(mySlice1, 50)
printSlice(mySlice1)
// len=9 cap=10 slice=[0 0 1 10 0 20 0 0 50]
// copy()
mySlice2 := make([]int, len(mySlice1), (cap(mySlice1))*2)
copy(mySlice2,mySlice1)
printSlice(mySlice2)
// len=9 cap=20 slice=[0 0 1 10 0 20 0 0 50]
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
集合 Map
像是 JS/TS 的Object,且無Slice的cap(capacity)範圍
// 默認的 map default: nil 空值
// 使用 var
var obj map[string][string]
// 使用 make
obj := make(map[string][string])
func printMap(x map[string]string) {
fmt.Printf("len=%d ,map=%v\n", len(x), x)
}
func main() {
countryCapitalMap := make(map[string]string)
countryCapitalMap["France"] = "巴黎"
countryCapitalMap["Italy"] = "羅馬"
countryCapitalMap["Japan"] = "東京"
printMap(countryCapitalMap)
// len=3 ,map=map[France:巴黎 Italy:羅馬 Japan:東京]
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap[country])
// Italy 首都是 羅馬
// Japan 首都是 東京
// France 首都是 巴黎
}
capital, ok := countryCapitalMap["American"]
if ok {
fmt.Println("American 的首都是", capital)
} else {
fmt.Println("American 的首都不存在")
}
// American 的首都不存在
delete(countryCapitalMap, "France") /*删除元素*/
printMap(countryCapitalMap)
// len=2 ,map=map[Italy:羅馬 Japan:東京]
}
範圍 Range
range 是 for 的其中一個使用方式
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
/* 若沒使用 i 可用 _ 去省略*/
for i, num := range nums {
fmt.Printf("index=%d num=%d\n", i, num)
}
}
接口 Interface
如果是 Javascript 學習者 推薦先往 TypeScript 學習,網上很多範例剛開始我卡到不行,interface 剛開始學的初心者簡直羅生門吧我想。
必需說,沒有 interface 也可以完成 商業邏輯的 code 先完成商業邏輯,再來改善即可。
建議先學會 Struct 再學 Interface,然後熟透 再 Pointer 混進使用,才不會搞混亂!
type Phone interface {
call()
}
type Nokia struct {
}
func (nokia Nokia) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func phoneCall(p Phone) {
p.call()
}
func main() {
fmt.Println("This use Struct Call.")
phone1 := Nokia{}
phone1.call()
phone2 := IPhone{}
phone2.call()
fmt.Println("This use Interface Call.")
phoneCall(Nokia{})
phoneCall(IPhone{})
}
併發 Routine
這項功能,算是我在學習 Go 這個語言覺得最酷的地方 好文分享:
go funcName(value type)
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("1")
say("2")
}
// 會沒有固定的先後順序輸出 1 與 2
最多執行與 CPU 相等的 Goroutine,取得CPU數目: runtime.NumCPU()
channel 通道
ch1 := make(chan int) // 創建 channel (ReadWrite)
ch1 <- v // v 發送至 ch (Write)
v := <-ch1 // ch 收 Data 賦值給 v (Read)
// 緩衝區,但注意 緩衝區容量有限,一定要有接收者把 Data 拿走,否則就會阻塞,發送者也無法送出
ch2 := make(chan int, 10)
close(ch2) // 關閉 channel
錯誤 Error
這地方偏向寫 Log
func example(num int) (bool, error) {
if int < 0 {
return false, errors.New("num < 0")
}
return true, _
}
func main(){
_, err := example(-1)
if err != nil {
fmt.Sprintf("Error: %s", err.Error())
}
}
defer / panic / recover
錯誤處理,以前用 try…catch…finally 往回吐,這三個的組成方式就很像是這項功能。
推遲 defer
- 後進先出
- 可用於 func 回傳變數
- 當 func 結束最後執行 像 try…catch…finally
func main() {
language := "中文最高"
defer fmt.Print(language + "\n")
language = "English the bast"
fmt.Print("那個 ,")
// 那個 , 中文最高
}
func c() (i int) {
defer func() { i++ }() // 先出
return 1 // 後進
// 2
}
恐慌 panic
遇到這Func 程式直接 crash ,立刻終止當前的 process
func panicFunc(){
panic('Message')
}
func main(){
fmt.Plantln("before.")
panicFunc() // exit
fmt.Plantln("after.")
}
恢復 recover
func main() {
f()
fmt.Println("Returned normally from f.") // 6. 最後輸出
}
func f() {
defer func() { // 3. defer 類似 finally() 被觸發
r := recover() // 4. 恢復
if r != nil {
fmt.Println("Recovered in f", r)
}
}() // 5. 結束
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
panicNumber := fmt.Sprintf("%v", i)
fmt.Println("Panicking!", panicNumber)
panic(panicNumber) // 2. 執行恐慌
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1) // 1. 重複執行
}
重點整理
Array Slice Map 三角關係
// slice
sliceExample := []int{1, 2, 3}
// map
mapExample := map[int][string]{ 1: "one", 2: "two", 3: "three" }
- Array 指的是講 {} 這裡面的 val
- delete() 只有 Map 可以使用, Slice 不能刪除只能切區段
- append() 只有 Slice 可以使用, Map 直接
varMap[type] = val
定義即可 - Slice 有範圍限定 length 最多只能多少
- Map 無範圍限定 length 像似 JS的Array 無限制 append
goroutine 使用時機
- 於
go func(){}
中若賦予變數傳出會因此順序大亂,由於線程在過程中會同時對變數做賦值,而導致過程亂掉,可用sync.Mutex
的Lock()
和Unlock()
,去做佇列鎖。 - 若是兩個
goroutine
線程要交換訊息,waitGroup + Lock
會比較好用。