標識符:html
是用戶或系統定義的有意義單詞組合,或單詞與數字組合(具體意義有定義者決定) python
標識符以字母下劃線開頭,大小寫敏感,好比:boy, Boy, _boy, _(匿名變量,用來忽略結果)程序員
標識符命名規範:在習慣上,Go語言程序員推薦使用駝峯式命名,當名字有幾個單詞組成的時優先使用大小寫分隔,而不是優先用下劃線分隔。所以,在標準庫有QuoteRuneToASCII和parseRequestLine這樣的函數命名,可是通常不會用quote_rune_to_ASCII和parse_request_line這樣的命名。而像ASCII和HTML這樣的縮略詞則避免使用大小寫混合的寫法,它們可能被稱爲htmlEscape、HTMLEscape或escapeHTML,但不會是escapeHtml。數據庫
關鍵字:編程
是 Go 語言提供的有特殊含義的符號,也叫作「保留字」數組
系統保留關鍵字:安全
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthough | if | range | type |
continue | for | import | return | var |
常量數據結構
常量使用 const 修飾,表示是隻讀的,不能修改多線程
const 只能修飾 boolean,number(int相關,浮點數,complex)和 string 類型閉包
語法:const identifier [type] = value(type 可省略)
優雅寫法:
const( name string = "skier" age int = 10 salary int = 5000 / 2 // gender boolean = getGender() // const不能從函數中獲取 )
常量由於在編譯期肯定,因此能夠用於數組聲明:
const size = 4 var arrayA [size]int
變量
聲明一個變量:var identifier [type]
// 聲明變量並賦值 var a int = 100 // 簡寫(自動推導類型) a := 100
優雅寫法:
var ( name string = "johny" age int = 10 )
默認值:
做用域:從定義變量的代碼行開始,一直到直接所屬的大括號結束爲止(全局變量除外)
在編程中,變量在其實現了變量的功能後,做用範圍越小,所形成的問題可能性越小,每個變量表明一個狀態,有狀態的地方,狀態就會被修改,函數的局部變量只會影響一個函數的執行,但全局變量可能會影響全部代碼的執行狀態,所以限制變量的做用範圍對代碼的穩定性有很大的幫助
數字類型:int8, int16, int32, int64, uint8, uint16, uint32, uint64
bool 類型:ture, false (bool 型沒法參與數值運算,也沒法與其餘類型進行轉換。)
浮點類型:float32, float64
字符類型:byte
字符串類型:字符串實現基於 UTF-8 編碼
"" 雙引號,定義單行字符串
`` 反引號,定義多行字符串(在這種方式下,反引號間換行將被做爲字符串中的換行,可是全部的轉義字符均無效,文本將會原樣輸出)
多行字符串通常用於內嵌源碼和內嵌數據等
類型轉換
格式:type(variable)
var a int = 8 var b int32 = int32(a)
浮點數轉換成 int 類型,精度會丟失
var c float32 = math.Pi fmt.Println(int(c))
int32 轉換成 int16,數據會截斷
// 初始化一個32位整型值 var a int32 = 1047483647 // 輸出變量的十六進制形式和十進制值 fmt.Printf("int32: 0x%x %d\n", a, a) // 將a變量數值轉換爲十六進制, 發生數值截斷 b := int16(a) // 輸出變量的十六進制形式和十進制值 fmt.Printf("int16: 0x%x %d\n", b, b) 結果是: int32: 0x3e6f54ff 1047483647 int16: 0x54ff 21759
字符串轉義符
轉移符 | 含 義 |
---|---|
\r | 回車符(返回行首) |
\n | 換行符(直接跳到下一行的同列位置) |
\t | 製表符 |
\' | 單引號 |
\" | 雙引號 |
\\ | 反斜槓 |
操做符
數字操做符:+, -, *, /, %
比較運算符:>, >=, <, <=, ==, !=
字符串操做
拼接:
var str1 string = "hello" var str2 string = "world" var str string = str1 + str2 // var str string = fmt.Sprintf("%s%s", str1, str2)
值類型
本質上是原始類型,變量直接儲存值,內存一般在棧中分配,包括 int, float, bool, string 以及數組和 struct(結構體)
對值類型進行操做,通常都會返回一個新建立的值,因此把這些值傳遞給函數時,其實傳遞的是一個值的副本
func main() { name:="張三" fmt.Println(modify(name)) fmt.Println(name) } func modify(s string) string{ s=s+s return s } //Output 張三張三 張三
以上是一個操做字符串的例子,經過打印的結果,能夠看到,原本 name 的值並無改變,也就是說,咱們傳遞的是一個副本,而且返回一個新建立的字符串
基本類型由於是值的拷貝,而且在對他進行操做的時候,生成的也是新建立的值,因此這些類型在多線程裏是安全的,咱們不用擔憂一個線程的修改影響了另一個線程的數據
引用類型
引用類型與值類型偏偏相反,它的修改能夠影響到任何引用到它的變量;變量存儲的是地址,這個地址存儲最終的值,一般在堆內存上分配,經過 GC 回收,包括 指針,select,map,chan 等
引用類型之因此能夠引用,是由於咱們建立引用類型的變量,實際上是一個標頭值,標頭值裏包含一個指針,指向底層的數據結構,當咱們在函數中傳遞引用類型時,其實傳遞的是這個標頭值的副本,它所指向的底層結構並無被複制傳遞,這也是引用類型傳遞高效的緣由。
if else 分支判斷
if condition1 { block1 } else if condition2 { block2 } else { block3 }
switch case 語句
func main(){ var variabel string = "a" switch variabel { case "a", "b": fmt.Println(variabel) // fallthrough // 會執行下一個case的語句塊 case "c": fmt.Println(variabel) default: fmt.Println("default output") } }
case 後邊的值能夠寫多個,是 或 的關係
case 語句塊末尾若是加上 fallthrough,會接着執行下一個 case 的語句塊
for 循環
for i:=0; i<100 ; i++ { fmt.Println("hello, world~") }
死循環(for)
func main(){ for { fmt.Println("hello, world~") time.Sleep(time.Second) } }
加判斷的 for 循環
func main(){ var i int for i<100 { fmt.Println(i) i += 1 } }
for range 語句
可使用 for range 遍歷數組、切片、字符串、map 及通道(channel)。經過 for range 遍歷的返回值有必定的規律:
遍歷數組:
var arrayA = [3]string{"hammer", "soldier", "mum"} for index, value := range arrayA { fmt.Println(index, value) } 運行結果: 0 hammer 1 soldier 2 mum
用匿名標識符忽略 index
var arrayA = [3]string{"hammer", "soldier", "mum"} for _, value := range arrayA { fmt.Println(value) } 運行結果: hammer soldier mum
固然,for 循環中也可以支持:break, continue
goto 語句
能夠經過標籤進行代碼間的無條件跳轉,goto語句能夠快速跳出循環 或 實現一樣的邏輯 有必定的幫助:
快速跳出循環:
func main(){ for i:=0; i<=100; i++{ for j:=0; j<=100; j++{ if j == 10 { // 直接跳轉到標籤 goto breakHere } } } breakHere: fmt.Println("hello world~") } 運行結果: hello world~
使用 goto 集中處理錯誤:
err := firstCheckError() if err != nil { goto onExit } err = secondCheckError() if err != nil { goto onExit } fmt.Println("done") return onExit: fmt.Println(err) exitProcess()
Go 語言支持普通函數、匿名函數和閉包
普通函數聲明:func 函數名(參數列表) (返回值列表) {函數體}
不支持重載,一個源文件內不能有兩個相同名稱的函數
函數是一等公民,也是一種類型,能夠賦值給變量
函數的傳參方式:
注意:不管是值傳遞仍是引用傳遞,傳遞給函數的都是變量的副本,不過,值傳遞是對值的拷貝,引用傳遞是地址的拷貝,通常來講,地址拷貝更爲高效,而值拷貝取決於拷貝對象的大小,對象越大,則性能越低
返回值命名:
返回值不須要定義,直接使用(命名的返回值變量的默認值爲類型的默認值,即數值爲 0,字符串爲 "",布爾爲 false、指針爲 nil)
func calc(a int, b int) (c int) { c = a + b return c }
可變長參數:
可變參數變量是一個包含全部參數的切片
func calc(a int, b int, arg... int) { fmt.Println(arg[0]) }
defer 的用途:
關閉文件句柄
注意:不能將這一句代碼放在第3行空行處,一旦文件打開錯誤,f將爲空,在延遲語句觸發時,將觸發宕機錯誤
func read(){ file err := open(filename)
if err != nil{ return } defer file.Close() }
鎖資源的釋放
func lock(){ mc.Lock() defer mc.Unlock() }
數據庫鏈接的釋放
func connect(){ conn := openDatabase() defer conn.Close() }
調用函數
函數在定義後,能夠經過調用的方式,讓當前代碼跳轉到被調用的函數中進行執行。調用前的函數局部變量都會被保存起來不會丟失;被調用的函數結束後,恢復到被調用函數的下一行繼續執行代碼,以前的局部變量也能繼續訪問
一個函數在內部調用本身,就叫作遞歸,下面來舉兩個遞歸函數的Demo
遞歸求階乘
func calc(n int) int { if n <= 1{ return 1 } return calc(n-1) * n } func main(){ result := calc(5) fmt.Println(result) } 運行結果: 120
斐波拉契數
func fab(n int) int { if n<=1{ return 1 } return fab(n-1) + fab(n-2) } func main(){ var n int = 6 var fabCount int for i:=0; i<=n; i++{ fabCount += fab(i) } fmt.Println(fabCount) } 運行結果: 33
匿名函數沒有函數名,只有函數體,能夠直接被當作一種類型賦值給函數類型的變量,匿名函數也每每以變量的方式被傳遞
匿名函數常常被用於實現回調函數、閉包
定義一個匿名函數:
func(str string){ fmt.Println("hello", str) }("world") 運行結果: hello world
也能夠將匿名函數賦值給變量:
f := func(str string){ fmt.Println("hello", str) } f("world")
匿名函數當作參數:
func visit(sliceA []int, f func(int)){ for _, value := range sliceA { f(value) } } func main(){ var sliceA []int = []int{1,2,3,4,5} f := func(a int){ fmt.Print(a) } visit(sliceA, f) } 運行結果: 12345
使用匿名函數實現操做封裝:
func main(){ var mapA map[string]func() mapA = map[string]func(){ "fire": func(){ fmt.Println("chicken fire") }, "run": func(){ fmt.Println("soldier run") }, "fly": func(){ fmt.Println("angel fly") }, } // fmt.Println(mapA) // 接收命令行參數,key,默認值,幫助 var skill *string = flag.String("skill", "", "skill type") flag.Parse() f, err := mapA[*skill] if err == true { f() } else { fmt.Println("skill not fount") } } 運行效果: $ go run main.go --skill=fly angel fly $ go run main.go --skill=run soldier run
閉包是引用了自由變量的函數,被引用的自由變量和函數一同存在,即便已經離開了自由變量的環境 也不會被釋放或者刪除,在閉包中能夠繼續使用這個自由變量(閉包(Closure)在某些編程語言中也被稱爲 Lambda 表達式)
簡單的說:
閉包 = 函數 + 引用環境
同一個函數與不一樣的引用環境組合,能夠造成不一樣的實例,如圖:
實現一個簡單的閉包:
func closureFunc(str string) func(){ wapper := func(){ fmt.Println("hello", str) } return wapper } func main(){ f := closureFunc("world~") // 調用閉包函數 f() } 運行結果: hello world~
累加器的實現(閉包的記憶效應)
func accumulate(num int) func() int { wapper := func() int { num += 1 return num } return wapper } func main(){ accumulator := accumulate(10) ret1 := accumulator() fmt.Println(ret1) ret2 := accumulator() fmt.Println(ret2) } 運行結果: 11 12
// 任何一個代碼源文件隸屬於一個包 package main //import 關鍵字,同一個包內的函數能夠直接調用,不一樣包中的函數經過 包名.函數名 的方式調用 import ( "fmt" "/go_dev/test" ) // 初始化函數 func init(){ fmt.Println("執行初始化操做") } // main 程序入口 func main(){ fmt.Println("hello, world~") }
init 函數
每一個源文件均可以包含一個 init 函數,會自動被編譯器執行
包訪問控制規則
程序執行順序(棧) *
練習1:寫一個函數,對於一個整數n,求出全部兩兩相加等於n的組合,好比 n=5
package main import ( "fmt" ) func calc(n int){ for i:=0; i<=n; i++ { num := n - i fmt.Printf("%d+%d=%d\n", i, num, n) } } func main(){ calc(5) } 結果: 0+5=5 1+4=5 2+3=5 3+2=5 4+1=5 5+0=5
練習2:一個程序包含 add 和 main 兩個包,add 包中有兩個變量 Name 和 age,在 main 中訪問 Name 和 age 變量,打印輸出
首先在 go_dev 下新建一個目錄 example(go_dev 目錄在環境變量 GOPATH 下 src 目錄,go編譯器根據系統路徑會找到 go_dev 目錄下的包)
add包下寫 add.go
package add var Name string = "skier" var age int = 19
main包下寫 main.go
package main import ( "go_dev/example/add" // a "go_dev/example/add" // 包別名的應用 "fmt" ) func main(){ fmt.Println(add.Name) fmt.Println(add.age) // 不能訪問 }
1.判斷101~200之間有多少個素數,並輸出打印
package main import ( "fmt" ) func main(){ for i:=101; i<=200; i++{ var flag bool = true for j:=2; j<i; j++{ if (i % j == 0) { flag = false } } if (flag == true){ fmt.Println("素數i:", i) } } }
2.打印出101~999中全部的水仙花數,所謂水仙花數是指一個三位數,其各位數字的立方和等於該數自己,例如153是一個水仙花數,1*1*1 + 5*5*5 + 3*3*3 = 153
package main import ( "fmt" ) func narcissisticNum(num int) bool { var g int = num % 100 % 10 var s int = num % 100 / 10 var b int = num / 100 //fmt.Println(g, s, b) var cube int = g*g*g + s*s*s + b*b*b return num == cube } func main(){ for i:=100; i<=999; i++{ result := narcissisticNum(i) if result == true { fmt.Println("水仙花數是:", i) } } }
3.對於一個數n,求n的階乘之和,即:1! + 2! + ... + n!
package main import ( "fmt" ) func summation(n int) int { var result int for i:=1; i<=n; i++ { var tmpe int = 1 for j:=1; j<=i; j++{ tmpe *= j } result += tmpe } return result } func main(){ var num int = 5 var result int = summation(num) fmt.Println("sum result:", result) }
4.在終端打印 9*9 乘法表
package main import( "fmt" ) func multiplication(){ for column:=1; column<=9; column++{ for row:=1; row<=column; row++{ fmt.Printf("%d*%d=%d\t", column, row, column*row) } fmt.Println() } } func main(){ multiplication() }
5.一個數若是剛好等於它的因子之和,這個數就稱之爲「完數」,例如:1+2+3=6,在終端輸出1000之內的全部完數
package main import( "fmt" ) func showPerfect(n int){ for i:=1; i<n; i++{ var sum int for j:=1; j<i; j++{ if i % j == 0{ sum += j } } if sum == i{ fmt.Printf("完數:%d \n", i) } } } func main(){ const num int = 1000 showPerfect(num) }
6.輸入一個字符串,判斷其是否爲「迴文」,迴文字符串是指從左到右讀,與從右到左讀是徹底相同的字符串
package main import ( "fmt" ) func isReverse(str string) bool { // 轉爲字符 char := []rune(str) var length int = len(char) for i:=0; i<=length / 2 - 1; i++{ if char[i] != char[length-1-i]{ return false } } return true } func main(){ var input string fmt.Scanf("%s", &input) result := isReverse(input) fmt.Println("result:", result) }
7.輸出一行字符串,分別統計其中英文字母,空格,數字和其它符號的個數
package main import ( "fmt" "bufio" "os" ) func count(str string) (wordCount, spaceCount, numCount, otherCount int) { chars := []rune(str) for _, value := range chars{ switch { case value >= 'a' && value <= 'z': wordCount += 1 case value >= 'A' && value <= 'Z': wordCount += 1 case value == ' ': spaceCount += 1 case value >= '0' && value <= '9': numCount += 1 default: otherCount += 1 } } return } func main(){ reader := bufio.NewReader(os.Stdin) // 讀取一行的內容 result, _, error := reader.ReadLine() if error == nil{ wordCount, spaceCount, numCount, otherCount := count(string(result)) fmt.Printf("wordCount:%d\nspaceCount:%d\nnumCount:%d\notherCount:%d\n", wordCount, spaceCount, numCount, otherCount) } }
8.計算兩個大數相加的和,這兩個大數會超過 int64 的表示範圍