您好,登錄后才能下訂單哦!
Go語言中定義函數使用fun關鍵字,具體格式為
func 函數名(參數)(返回值){
函數體
}
其中:
1.函數名:由字母,數字,下劃線組成。但函數名的第一個字母不能是數字。在同一個包內,函數名不能重名。
2.參數:參數由變量名和變量類型組成。
3.返回值:可以只寫返回值類型,多個返回值必須用()包裹,并用,分隔。
4.函數體:實現指定功能的代碼塊。
定義一個求兩個數和的函數:
func intSum(x int, y int) int {
return x + y
}
函數的參數和返回值是可選的,我們也可以不傳參也沒有返回值。
func sayHello() {
fmt.Println("Hello vita")
}
定義了函數后,可以通過 函數名()的方式調用函數。
調用有返回值的函數時,可以不接收返回值。
package main
import "fmt"
func sayHello(){
fmt.Println("hello vita")
}
func main() {
sayHello()
}
結果:
hello vita
函數的參數中如果相鄰變量的類型相同,則可以省略類型。
package main
func intSum(x,y int)int{
return x+y
}
func main() {
intSum(1,2)
}
intSum函數有兩個參數,x,y的類型都是int,因此可以省略x的類型,因為y后面有類型說明。
可變參數即參數數量不固定。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
可變參數放在固定參數的后面
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
Go語言中支持多返回值,函數如果有多個返回值,必須使用()把所有返回值包裹起來。
package main
func calc(x,y int)(int,int){
sum :=x+y
sub:=x-y
return sum,sub
}
func main() {
calc(3,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)
}
全局變量是定義在函數外部的變量,他在程序整個運行周期內都有效。在函數中可以訪問到全局變量。
package main
import "fmt"
var num int64=100
func testGlobalVar(){
fmt.Printf("num=%d\n",num)
}
func main() {
testGlobalVar() //num=100
}
函數內定義的變量,無法在函數外使用。
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
}
我們可以使用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
我們可以聲明函數類型的變量并且為該變量賦值:
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
}
高階函數分為函數作為參數和函數作為返回值。
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
}
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
函數可以作為返回值,但是在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
閉包,在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
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語句能非常方便的處理資源釋放問題。比如:資源清理、文件關閉、解鎖及記錄時間等。
在Go語言的函數中,return語句在底層并不是原子操作,它分為給返回值賦值和RET指令兩步。
defer語句執行時機就在賦值操作后,RET指令執行前。具體如圖:
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())
}
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
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的語句之前定義。
/*
你有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
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。