這一節看下Golang的基礎文法...嘛...如果懶得看的話,記得以下兩點就好:
1 Golang的變數、函數參數等等涉及到類型的聲明,都是變數名寫在前,類型寫在後的(和正統C系文法剛好相反)
2 迴圈語句for通吃所有
然後,就開始啦~
Go語言基本文法
package mainimport ( "fmt")func main() { fmt.Println("Hello Azen٩(●˙▿˙●)۶…⋆ฺ")}
作用:使用fmt這個庫運行輸出
定義變數
var 關鍵字
func variableZeroValueDefine() { var a int var s string fmt.Printf("%d %q\n", a, s)}
知識點:
- 變數名寫在前面,變數類型寫在後面
原因:在定義變數的時候,往往是先想到名字,再想到變數類型
- 定義完變數後,變數都是有預設初值的。
2.1. int類型的初值為0
2.2. string類型的初始為空白串 - 使用%q可列印空串,展示結果為→ ""
同時定義變數並賦初值
注意:變數定義後,一定要用到,否則編譯器報錯。
func varibaleInitial() { var a, b int = 1, 2 var s string = "猜猜我是誰" fmt.Println(a, b, s)}
此例中,int 和 string是多餘的,編譯器可以做類型推斷 - Type Deduction
類型推斷
func varibaleTypeDeduction() { var a, b, c, s = 1, 2, true, "ㄟ..." b = 666 fmt.Println(a, b, c, s)}
:=定義並賦初值
更推薦用這種方式定義並初始設定變數
func varibaleShortcut() { a, b := 233, 666 s := "老鐵蛤蛤蛤" fmt.Println(a, b, s)}
在函數外定義變數
var a, b intvar ss = "總想搞個大新聞"
在函數外面定義的變數,不是全域變數,而是包內變數。
Go語言木有全域變數的說法。
注意:在函數外定義變數不能用:=的方式,一定要被var關鍵字修飾
使用()定義多個包內變數
可以把變數定義在括弧中,以減少var關鍵字的使用數量
var ( s string a, b int // a, b := 666, 233 // 全域變數不能用:=定義)var ss = "總想搞個大新聞"
內建變數類型
- bool,string
- (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uintptr
- 沒跟長度,則長度按作業系統位元而定
- uintptr是個指標
- 字元型
- byte
- rune
- 不是char,char只有1個位元組
- rune的長度為32位,4位元組,為應對全球化的字元
- 浮點數
- float32,float64
- complex64,complex128
- 複數,有實部和虛部
- complex64的實部和虛部為32位
- complex128的實部和虛部為64位
- 把複數直接作為內建變數的類型
強制類型轉換
Golang只有強制類型轉換,沒有隱式類型轉換
樣本:計算斜邊的長度
a = 3 b = 4 求斜邊c
func triangle() { var a, b = 3, 4 var c int c = int(math.Sqrt(float64(a * a + b * b))) fmt.Println(c)}
math.Sqrt函數的入參和出參都是float64,所以需要強制轉換。
常量的定義
關鍵字:const
常量類型可規定也可不規定
- 如果不規定類型,常量會像「文本替換」功能一樣(類似宏定義),到使用它的地方再決定類型
Golang中不會把常量全部大寫,因為首字母大寫在golang中代表範圍為public
func consts() { const ( filename = "abc.txt" a, b = 3, 4 ) var c int c = int(math.Sqrt(a * a + b * b)) fmt.Println(filename, c)}
*註:上面的a*a就不用強制轉為float64,因為被當成類似「宏替換」了
枚舉類型
基本定義:就定義成一組const就好
func enums() { const ( azen = 0 daker = 1 buran = 2 ) fmt.Println(azen, daker, buran)}
iota自增值
定義枚舉
func enums() { const ( azen = iota daker _ buran ) fmt.Println(azen, daker, buran)}
iota參與運算
const ( b = 1 << (10 * iota) kb mb gb tb pb)
條件陳述式
if語句
func bounded(v int) int { if v > 100 { return 100 } else if v < 0 { return 0 } else { return v }}
特點:if條件不需要用括弧包裹
樣本:
func ReadFile(filename string) ([]byte, error)
返回兩個值:
- [ ]byte 是讀取到的檔案內容數組 - 使用%s列印內容
- error 是出錯資訊
func fileRead() { const filename = "abc.txt" contents, err := ioutil.ReadFile(filename) if err != nil { fmt.Println(contents) } else { fmt.Println(err) }}
使用;簡化條件
func fileRead() { const filename = "abc.txt" if contents, err := ioutil.ReadFile(filename); err != nil { fmt.Println(contents) } else { fmt.Println(err) }}
在條件中可以用:=做聲明和賦值
出了if的範圍就不能訪問如上定義的content和err了
switch語句
基本用法
func eval(a, b int, op string) int { var result int switch op { case "+": result = a + b case "-": result = a - b case "*": result = a * b case "/": result = a / b default: panic("不支援的操作類型") } return result}
特點:
- 不需要加break,預設都有break.
- 如果不需要break,手動加fallthrough - 這一點和swift一樣
panic
拋出一個錯誤 - 類似於NSError的raise
switch後不接運算式
在case中加入條件即可
func grade(score int) string { var g string switch { case score < 0 || score > 100: panic(fmt.Sprintf("Wrong score: %d", score)) case score < 60: g = "F" case score < 80: g = "B" case score < 90: g = "A" case score <= 100: g = "SSR" } return g}
迴圈語句
for語句
func accumulate() { sum := 0 for i := 0; i < 100; i++ { sum++ } fmt.Println(sum)}
栗子:轉二進位
轉二進位的思路:不斷模數,獲得的數字往前加
func convertToBin(number int) string { result := "" for ; number > 0; number /= 2 { lsb := number % 2 result = strconv.Itoa(lsb) + result } return result}
strconv.Itoa → 將int轉為string
for的while用法(只有結束條件)
func pintFile(filename string) { file, err := os.Open(filename) if err != nil { panic(err) } scanner := bufio.NewScanner(file) // func (s *Scanner) Scan() bool - 在讀完或報錯的時候,會返回false for scanner.Scan() { }}
當for 語句中,省略其實條件、遞增條件,只有結束條件的時候,類似於while
for省略所有運算式 - 死迴圈
func forever() { for { fmt.Println("Bilibili") }}
Golang中經常要用到死迴圈,goroutine的肚子裡就是死迴圈。好多死迴圈的goroutine在互相進行通訊。 - 這和iOS開發中的runloop是一樣一樣的
函數定義
func eval(a, b int, op string) int
函數名在前,類型在後。和變數定義思路一致
基本樣本
func convertToBin(number int) string { result := "" for ; number > 0; number /= 2 { lsb := number % 2 result = strconv.Itoa(lsb) + result } return result}
函數返回多個值樣本
帶餘除法
func div(a, b int) (int, int) { return a / b, a % b}
給傳回值起名字
好處是可讀性更強,函數調用者可以很方便的知道傳回值是啥...
func div(a, b int) (q, r int) { return a / b, a % b}
如下寫法也可以正確返回q和r,但是可讀性不強,不建議這樣寫
func divNoRecommend(a, b int) (q, r int) { q = a / b r = a % b return}
多傳回值只使用一個
result, _ := div(10, 7)
多值返回的習慣用法
第一個值返回正常值,第二個值返回error
函數和函數式編程
函數在Golang中是一等公民,函數可以作為參數、傳回值
函數作為參數
func apply(op func(a, b int) int, a, b int) (result int) { return op(a, b)}
列印下op看看它是誰
- 拿函數名
- 需要用reflect.ValueOf(op).Pointer()拿到這個函數的指標
- 使用runtime擷取函數名runtime.FuncForPC(p).Name()
func apply(op func(a, b int) int, a, b int) (result int) { p := reflect.ValueOf(op).Pointer() opName := runtime.FuncForPC(p).Name() fmt.Println(opName) return op(a, b)}
匿名函數作為函數參數
fmt.Println( apply(func (a, b int) int { return a + b }, 10, 20),)
Golang沒有花哨的lambda運算式
函數其他說明
可變參數列表
可變參數列表的使用方法,可以和數組一樣用
func sum(numbers ...int) int { result := 0 for i := range numbers { result += numbers[i] } return result}
沒有預設參數、選擇性參數概念
指標
func pointerDefine() { var a = 0 var pa *int = &a *pa = 0x66CCFF fmt.Println(a)}
特點
- 指標不能運算
- C可以拿到指標的頭指標做加法運算 - 步長由指標類型決定
函數參數傳遞
C、C++、OC可以值傳,也可以引用傳
Java和Python基本上都是引用傳遞
Golang只有值傳遞一種方式
凡調函數,參數都會拷貝一份 - 和指標配合,實現相當引用傳遞的效果
舉例:交換兩個變數的值
func swap(a, b *int) { *a, *b = *b, *a}