1.函數
1.1函數定義
Go語言中定義函數使用fun關鍵字,具體格式爲
func 函數名(參數)(返回值){
函數體
}
其中:
1.函數名:由字母,數字,下劃線組成。但函數名的第一個字母不能是數字。在同一個包內,函數名不能重名。
2.參數:參數由變量名和變量類型組成。
3.返回值:能夠只寫返回值類型,多個返回值必須用()包裹,並用,分隔。
4.函數體:實現指定功能的代碼塊。
定義一個求兩個數和的函數:
func intSum(x int, y int) int {
return x + y
}
函數的參數和返回值是可選的,咱們也能夠不傳參也沒有返回值。
func sayHello() {
fmt.Println("Hello vita")
}
1.2函數的調用
定義了函數後,能夠經過 函數名()的方式調用函數。
調用有返回值的函數時,能夠不接收返回值。
package main
import "fmt"
func sayHello(){
fmt.Println("hello vita")
}
func main() {
sayHello()
}
結果:
hello vita
1.3參數
1.3.1類型簡寫
函數的參數中若是相鄰變量的類型相同,則能夠省略類型。
package main
func intSum(x,y int)int{
return x+y
}
func main() {
intSum(1,2)
}
intSum函數有兩個參數,x,y的類型都是int,所以能夠省略x的類型,由於y後面有類型說明。
1.3.2可變參數
可變參數即參數數量不固定。Go語言中的可變參數經過在參數後面加...來標識。
注意:可變參數一般要做爲函數的最後一個參數。
package main
import "fmt"
func intSum(x ...int)int{
fmt.Println(x)//x是一個切片
sum:=0
for _,value :=range x{
sum+=value
}
return sum
}
func main() {
sum1 := intSum(1,2,3,4,5)
sum2 := intSum(1,2,3,4,5,6,7)
sum3 := intSum(1,2,3,4,5,6,7,8,9)
fmt.Println(sum1)
fmt.Println(sum2)
fmt.Println(sum3)
}
結果:
[1 2 3 4 5]
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7 8 9]
15
28
45
1.3.3固定參數搭配可變參數
可變參數放在固定參數的後面
package main
import "fmt"
func intSum(x int,y ...int)int{
sum:=x
for _,value :=range y{
sum+=value
}
return sum
}
func main() {
sum1 := intSum(1,2,3,4,5)
sum2 := intSum(1,2,3,4,5,6,7)
sum3 := intSum(1,2,3,4,5,6,7,8,9)
fmt.Println(sum1)
fmt.Println(sum2)
fmt.Println(sum3)
}
結果:
15
28
45
1.4返回值
1.4.1多返回值
Go語言中支持多返回值,函數若是有多個返回值,必須使用()把全部返回值包裹起來。
package main
func calc(x,y int)(int,int){
sum :=x+y
sub:=x-y
return sum,sub
}
func main() {
calc(3,2)
}
1.4.2返回值命名
函數定義時,能夠給返回值命名,並在函數體中直接使用這些變量,最後經過return關鍵字返回。
package main
func calc(x,y int)(sum int,sub int){
sum =x+y
sub=x-y
return
}
func main() {
calc(3,2)
}
2.函數進階
2.1全局變量
全局變量是定義在函數外部的變量,他在程序整個運行週期內都有效。在函數中能夠訪問到全局變量。
package main
import "fmt"
var num int64=100
func testGlobalVar(){
fmt.Printf("num=%d\n",num)
}
func main() {
testGlobalVar() //num=100
}
2.2局部變量
函數內定義的變量,沒法在函數外使用。
func testLocalVar() {
//定義一個函數局部變量x,僅在該函數內生效
var x int64 = 100
fmt.Printf("x=%d\n", x)
}
func main() {
testLocalVar()
fmt.Println(x) // 此時沒法使用變量x
}
若是局部變量和全局變量重名,優先訪問局部變量。
package main
import "fmt"
//定義全局變量num
var num int64 = 10
func testNum() {
num := 100
fmt.Printf("num=%d\n", num) // 函數中優先使用局部變量
}
func main() {
testNum() // num=100
}
語句塊中定義的變量,一般咱們會在if條件判斷,for循環,switch語句上使用這種定義變量的方式。
func testLocalVar2(x, y int) {
fmt.Println(x, y) //函數的參數也是隻在本函數中生效
if x > 0 {
z := 100 //變量z只在if語句塊生效
fmt.Println(z)
}
//fmt.Println(z)//此處沒法使用變量z
}
for循環語句中定義的變量,也只在for語句塊中生效
func testLocalVar3() {
for i := 0; i < 10; i++ {
fmt.Println(i) //變量i只在當前for語句塊中生效
}
//fmt.Println(i) //此處沒法使用變量i
}
2.3定義函數類型
咱們可使用type關鍵字定義一個函數類型,具體格式以下:
type calculation func(int, int) int
上面語句定義了一個calculation類型,它是一種函數類型,接收兩個int參數,返回值類型爲int。
簡單的說,凡是知足這個條件的函數都是calculation類型的函數,例以下面的add和sub是calculation類型。
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
add和sub都能賦值給calculation類型的變量。
var c calculation
c = add
2.4函數類型變量
咱們能夠聲明函數類型的變量而且爲該變量賦值:
func main() {
var c calculation // 聲明一個calculation類型的變量c
c = add // 把add賦值給c
fmt.Printf("type of c:%T\n", c) // type of c:main.calculation
fmt.Println(c(1, 2)) // 像調用add同樣調用c
f := add // 將函數add賦值給變量f1
fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int
fmt.Println(f(10, 20)) // 像調用add同樣調用f
}
3.高階函數
高階函數分爲函數做爲參數和函數做爲返回值。
3.1函數做爲參數
package main
import "fmt"
func add(x,y int) int {
return x+y
}
func calc(x,y int, op func(int,int) int) int{
return op(x,y)
}
func main() {
ret2 := calc(10,20,add)
fmt.Println(ret2) //30
}
3.2函數做爲返回值
package main
import (
"errors"
"fmt"
)
func add(x,y int) int {
return x+y
}
func sub(x,y int) int {
return x-y
}
func do(s string) (func(int, int) int,error) {
switch s {
case "+":
return add,nil
case "-":
return sub,nil
default:
err := errors.New("沒法識別的操做符")
return nil,err
}
}
func main() {
addResult,err := do("+")
fmt.Println(addResult(1,2),err)
subResult,err := do("-")
fmt.Println(subResult(2,1),err)
informalResult,err := do(".")
fmt.Println(informalResult,err)
}
結果:
3 <nil>
1 <nil>
<nil> 沒法識別的操做符
Process finished with exit code 0
3.3匿名函數
函數能夠做爲返回值,可是在GO語言中,只能定義匿名函數。
匿名函數就是沒有函數名的函數,匿名函數的定義格式以下:
func(參數)(返回值){
函數體
}
匿名函數由於沒有函數名,因此不能像普通函數那樣調用,因此匿名函數須要保存到某個變量或者做爲當即執行函數。
匿名函數多用於實現回調函數和閉包。
package main
import "fmt"
func main() {
//將匿名函數保存到變量
add := func(x,y int) {
fmt.Println(x+y)
}
add(10,20) //經過變量調用匿名函數
//自執行函數:匿名函數定義完加()直接執行
func(x,y int){
fmt.Println(x+y)
}(10,20)
}
結果:
30
30
Process finished with exit code 0
3.4閉包
閉包,在Python中,是外部函數的返回值是內部函數的應用,內部函數使用了外部函數的變量,這就構成了閉包。
在Go語言中,也是一樣的。
adder函數的返回值是一個內部的匿名函數,該匿名函數引用了外部的變量x,而且x變量在f的生命週期內一直有效。
package main
import "fmt"
func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
f := adder()
fmt.Println(f(10))//10
fmt.Println(f(20))//30
fmt.Println(f(30))//60
f1 := adder()
fmt.Println(f1(40))//40
fmt.Println(f1(50))//90
}
結果:
10
30
60
40
90
Process finished with exit code 0
閉包進階示例1
package main
import "fmt"
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
f := adder2(10)
fmt.Println(f(10))//20
fmt.Println(f(20))//40
fmt.Println(f(30))//70
f1 := adder2(20)
fmt.Println(f1(40))//60
fmt.Println(f1(50))//110
}
結果:
20
40
70
60
110
Process finished with exit code 0
閉包進階示例2:
package main
import (
"fmt"
"strings"
)
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name,suffix){
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
textFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test"))
fmt.Println(textFunc("test"))
}
結果:
test.jpg
test.txt
Process finished with exit code 0
閉包進階示例3:
package main
import "fmt"
func calc(base int) (func(int) int,func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add,sub
}
func main() {
f1,f2 := calc(10)
fmt.Println(f1(1),f2(2))
fmt.Println(f1(3),f2(4))
fmt.Println(f1(5),f2(6))
}
結果:
11 9
12 8
13 7
Process finished with exit code 0
4.defer語句
4.1延遲調用特性
Go語言中的defer語句會將其後面跟隨的語句進行延遲處理。
在defer歸屬的函數即將返回時,將延遲處理的語句按defer定義的逆序進行執行。
即先被defer的語句最後被執行,最後被defer的語句,最早執行。
package main
import "fmt"
func main() {
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
結果:
start
end
3
2
1
Process finished with exit code 0
因爲defer語句延遲調用的特性,因此defer語句能很是方便的處理資源釋放問題。好比:資源清理、文件關閉、解鎖及記錄時間等。
4.2defer執行時機
在Go語言的函數中,return語句在底層並非原子操做,它分爲給返回值賦值和RET指令兩步。
defer語句執行時機就在賦值操做後,RET指令執行前。具體如圖:
4.3defer經典案例
package main
import "fmt"
// 關於defer的面試題
func f1() int {
x := 5
defer func() {
x++
}()
return x // 1. 返回值=5 2. x++ 3. RET指令 ==> 5
}
func f2() (x int) {
defer func() {
x++
}()
return 5 // 1. (彙編)返回值=x(5) 2. x++ 3.(彙編)RET ==> 6
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x // 1. (彙編)返回值=y(5) 2. x++ 3.(彙編)RET ==> 5
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5 //1. (彙編)返回值=x(5) 2. x++(函數內部的x) 3.(彙編)RET ==> 5
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
}
4.4defer面試題
package main
import "fmt"
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
}
提示:defer註冊要延遲執行的函數時該函數全部的參數都須要肯定其值
結果:
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4
Process finished with exit code 0
5.內置函數
5.1panic/recover
Go語言中,目前(Go1.12)是沒有異常機制的,可是使用panic/recover模式能夠處理錯誤。
panic能夠在任何地方引起,可是recover只有在defer調用的函數中有效。
package main
import "fmt"
func funcA() {
fmt.Println("func A")
}
func funcB() {
panic("func B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
在程序運行期間,funcB中引起了panic,致使程序崩潰,異常退出了。
這個時候,咱們能夠經過recover將程序恢復回來,繼續日後執行。
package main
import "fmt"
func funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
err := recover()
if err != nil{
fmt.Println("recover in B")
}
}()
panic("func B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
結果:
func A
recover in B
func C
Process finished with exit code 0
注意:
1.recover()必須搭配defer使用。
2.2.defer必定要在可能引起panic的語句以前定義。
6.練習題
/*
你有50枚金幣,須要分配給如下幾我的:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配規則以下:
a. 名字中每包含1個'e'或'E'分1枚金幣
b. 名字中每包含1個'i'或'I'分2枚金幣
c. 名字中每包含1個'o'或'O'分3枚金幣
d: 名字中每包含1個'u'或'U'分4枚金幣
寫一個程序,計算每一個用戶分到多少金幣,以及最後剩餘多少金幣?
程序結構以下,請實現 ‘dispatchCoin’ 函數
*/
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
func main() {
left := dispatchCoin()
fmt.Println("剩下:", left)
}
package main
import (
"fmt"
"strings"
)
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
func dispatchCoin() int{
count := 0
for _,value := range users{
eCount := strings.Count(strings.ToLower(value),"e")
iCount := strings.Count(strings.ToLower(value),"i")
oCount := strings.Count(strings.ToLower(value),"o")
uCount := strings.Count(strings.ToLower(value),"u")
personCount := eCount*1+iCount*2+oCount*3+uCount*4
fmt.Printf("Pserson:%s,coins:%d\n",value,personCount)
count += personCount
}
return coins-count
}
func main() {
left := dispatchCoin()
fmt.Println("剩下:", left)
}
結果:
Pserson:Matthew,coins:1
Pserson:Sarah,coins:0
Pserson:Augustus,coins:12
Pserson:Heidi,coins:5
Pserson:Emilie,coins:6
Pserson:Peter,coins:2
Pserson:Giana,coins:2
Pserson:Adriano,coins:5
Pserson:Aaron,coins:3
Pserson:Elizabeth,coins:4
剩下: 10
Process finished with exit code 0