# Day07 Go飆車囉-狂教基本語法(if, switch, for, log) 今天東西稍微多了點, 但忍耐一下,文章就快結束了。 各程式語言基本上大同小異 如果各位學過其他程式語言、已有程式底子, 那麼學`Golang`只剩下**適應Go的語法**。 > > 但是學習嘛,想是一回事、做是一回事 > 看完文章別看過就好,還是要自己動手從零開始的打異世界程式 > > 俗話說的好:學程式別無他法,唯有自己寫,才是自己的。 > > 法二:找個工具人 ## 語法斷句 在Go中,語法斷句需要分號`;` **但是你不用寫**(硬要寫也是可以啦)。 因為它會幫你產生,可以省鍵盤軸心壽命,夠佛。 > Like C, Go's formal grammar uses semicolons to terminate statements, > but unlike in C, those semicolons do not appear in the source. ```go a := 10; b := 20;; fmt.Println(a, b);;;;; ``` ## 條件判斷 #### 【if 條件】 條件直接寫,不用加小括號 (要加也是可以啦,不會報錯,佛心公司) ```go if Condition { ... } else { ... } ``` https://play.golang.org/p/DW0nTIuHWnk #### 【Switch】 一次做多個`if`的判斷 可以把**條件代入`case`中,也可以放在`switch`上,** 一魚可以兩吃,佛心公司。 `case`跟`case`之間**不用`break`**,更加直觀,佛! https://play.golang.org/p/hRVHoeW6_h9 ```go var i int = 2 switch i { case 1: fmt.Println("i is 1") case 2: fmt.Println("i is 2") case 3: fmt.Println("i is 3") default: fmt.Println("i is not 1, 2, 3") } //-------- var j int = 10 switch { case j == 1: fmt.Println("j is 1") case j == 2: fmt.Println("j is 2") case j == 3: fmt.Println("j is 3") default: fmt.Println("j is not 1, 2, 3") } ``` ## 迴圈 #### 【for迴圈】 `for loop` 完全不用小括號,省時省力,佛! > **for Expression; Condition; Operation { > ... > }** https://play.golang.org/p/o4EZytZSgt- ```go for i:=0; i<10; i++{ fmt.Println(i) } ``` #### 【while迴圈】 `Go` 沒有`While`關鍵字啦!記者標題! 但可以這樣用 > **for Condifion{ > ... > }** https://play.golang.org/p/xVLC_SsygHt ```go i := 0 for i < 10{ fmt.Println(i) i++ } ``` #### 【for迭代】 能夠遍歷整個物件變數, 另外用`for`迭代的話,就不能在裡頭**修改(新增、刪除)迭代物件**,會容易出問題 若真要修改物件,請用上面**for迴圈**的方式 > **for a, b := range Iterable{ > ... > }** https://play.golang.org/p/2N6FrtbpB6f ```go nums := []int{100, 99, 98} for index, num := range nums { fmt.Println(index, num) } for index := range nums { fmt.Println(index) } //-------- fruits := map[string]string{"a": "apple", "b": "banana"} for index, fruit := range fruits { fmt.Println(index, fruit) } for index := range fruits { fmt.Println(index) } ``` 單單一個`for`關鍵字有這麼多用途,佛! ## Log日誌 #### 【log.Print】 #### 【log.Println】 #### 【log.Printf】 Go的`log.Print`用法跟`fmt.Print`用法基本上一致 但`log`會在開一個線程出來,而`fmt`則是運行在主線程上 ```go func main() { for i:= 0; i <= 3; i++{ log.Println(i) fmt.Println(i) // time.Sleep(time.Millisecond * 10) log.Println("hi") fmt.Println("hi") } } ``` > 開線程這點在Windows的環境上看的出來(左);但在Playground及MacOS中運行起來是這樣(右) > ![Win10 vs MaxOS](https://i.imgur.com/33b1WBZ.png) #### 【log.SetFlag】 改變印出的時間格式 ```go log.SetFlags(0) log.SetFlags(1) 2020/09/13 log.SetFlags(2) 20:57:00 log.SetFlags(3) 2020/09/13 20:57:00 log.SetFlags(4) 20:57:00.862816 log.SetFlags(5) 2020/09/13 20:57:00.862816 ``` 也可以用常數名稱直接帶入`log.SetFlags(log.Ldate)` 關於SetFlags的參數 ```go const ( Ldate = 1 << iota // local time zone: 2009/01/23 Ltime //2 // local time zone: 01:23:23 Lmicroseconds //4 // microsecond : 01:23:23.123123. Llongfile //8 // full filename, line: /a/b/c/d.go:23 Lshortfile //16 // filename element, line: d.go:23 LUTC //32 // use UTC Lmsgprefix //64 // move the "prefix" from the beginning of the line to before the message LstdFlags = Ldate | Ltime //3 預設Flag // initial standard logger ) ``` 若想要同時印出 檔案名稱 `Lshortfile (16)` 及 日期 `Ldate(1)`, 則可直接相加數字,設定為`log.SetFlags(17)`,以此類推。 也可以用`|` **(或運算or)** `log.SetFlags(log.Lmsgprefix | log.LstdFlags)` 格式可以自己兜,任君挑選,**佛!** https://play.golang.org/p/IIoJo55I8sr ```go import ( "log" ) func main() { for i := 0; i <= 127; i++ { log.SetFlags(i) log.Print("log.SetFlags(", i, ")") } } ``` #### 【log.SetPrefix】 ```go log.SetPrefix("文字") ``` https://play.golang.org/p/gwCz6XNkjDx ``` go func main() { log.SetPrefix("安安,我是log ") log.Println("Hi") log.Fatalln("發生錯誤") } /* result: 安安,我是log 2009/11/10 23:00:00 Hi 安安,我是log 2009/11/10 23:00:00 發生錯誤 */ ``` #### 【log.Fatal】 #### 【log.Fatalf】 #### 【log.Fatalln】 用於發生錯誤時印出log並退出 點進去看go原始碼,退出的實作是多了`os.Exit(1)`這行 ```go func Fatal(v ...interface{}) { std.Output(2, fmt.Sprint(v...)) os.Exit(1) } ``` 要自製logger,[可以參考此處](https://golang.org/pkg/log/#Logger)