Go 語(yǔ)言過(guò)程式編程實(shí)驗(yàn)簡(jiǎn)介這節(jié)講解Go語(yǔ)言的各種語(yǔ)句,,控制結(jié)構(gòu),,以及如何使用這些語(yǔ)句進(jìn)行過(guò)程式編程,課程的內(nèi)容比較多,,但是都比較重要,,在編寫(xiě)Go程序的過(guò)程中會(huì)頻繁使用到。 一,、實(shí)驗(yàn)說(shuō)明本課程所有源代碼,,可以在XfceTerminal中通過(guò)以下方式克隆到實(shí)驗(yàn)環(huán)境:
二. Go語(yǔ)言語(yǔ)句基礎(chǔ)之所以先學(xué)習(xí)過(guò)程式編程,是因?yàn)樵贕o語(yǔ)言中面向?qū)ο缶幊桃彩墙⒃诿嫦蜻^(guò)程的基礎(chǔ)上的,。形式上講,Go語(yǔ)言需要使用分號(hào)(;) 來(lái)作為上下文語(yǔ)句的分隔結(jié)束符,。實(shí)際上在前面的代碼中我們可以看到在Go語(yǔ)言中很少使用分號(hào),那是因?yàn)榫幾g器會(huì)自動(dòng)在需要分號(hào)的地方加上分號(hào),。但是有兩個(gè)地方必須使用分號(hào),第一個(gè)是需要在一個(gè)行中放入一條或多條語(yǔ)句時(shí),,或者是在使用原始的 for 循環(huán)時(shí)。Go語(yǔ)言也支持多重賦值,,如
以上代碼中,先使用 1. 類(lèi)型轉(zhuǎn)換Go語(yǔ)言提供了一種在不同但相互兼容的類(lèi)型之間相互轉(zhuǎn)換的方式,,這種轉(zhuǎn)換非常有用并且是安全的。但是需要注意的是在數(shù)值之間進(jìn)行轉(zhuǎn)換可能造成其他問(wèn)題,如精度丟失或者錯(cuò)誤的結(jié)果,。以下是類(lèi)型轉(zhuǎn)換的語(yǔ)法:
幾個(gè)例子:
另外在Go語(yǔ)言中可以通過(guò) 2. 類(lèi)型斷言說(shuō)到類(lèi)型斷言就需要先了解下Go語(yǔ)言中的接口,。在Go語(yǔ)言中接口是一個(gè)自定義類(lèi)型,。它聲明了一個(gè)或者多個(gè)方法。任何實(shí)現(xiàn)了這些方法的對(duì)象(類(lèi)型)都滿(mǎn)足這個(gè)接口,。接口是完全抽象的,,不能實(shí)例化。
使用VIM創(chuàng)建源文件
運(yùn)行程序:
三. 分支和
|
語(yǔ)法 | 含義 |
---|---|
channel <- value | 發(fā)送value到通道中,,有可能阻塞 |
<-channel | 從通道中接收數(shù)據(jù) |
x := <-channel | 接收數(shù)據(jù)并賦值給x |
x, ok := <-channel | 功能同上,,同時(shí)檢查通道是否已關(guān)閉或者是否為空 |
select
語(yǔ)句在前面的課程中我們提到過(guò)select語(yǔ)句,用于監(jiān)聽(tīng)通道。其語(yǔ)法如下:
select {
case sendOrReceviae1: block1
...
case sendOrReceiveN: blockN
default: blockD
}
Go語(yǔ)言會(huì)從頭至尾的判斷每一個(gè)case
中的發(fā)送和接收語(yǔ)句,。如果其中任何一條語(yǔ)句可以執(zhí)行(即沒(méi)有被阻塞),,那就從那些可執(zhí)行的語(yǔ)句中任意選擇一條來(lái)使用。如果所有的通道都被阻塞,,那可能有兩種情況,。第一種,如果有default
語(yǔ)句,,那就會(huì)執(zhí)行default
語(yǔ)句,,同時(shí)程序的執(zhí)行會(huì)從select
語(yǔ)句恢復(fù)。第二種,,如果沒(méi)有default
語(yǔ)句,,則select
語(yǔ)句會(huì)一直阻塞,直到有一個(gè)通道可用
下面讓我們使用以上的相關(guān)知識(shí)進(jìn)行下練習(xí),,使用VIM創(chuàng)建源文件goroutine_channel_t.go
,,輸入如下源代碼:
package main
import (
"fmt"
"math/rand"
)
func main() {
channels := make([]chan bool, 6) // 創(chuàng)建一個(gè)類(lèi)型為chan bool的切片,每一項(xiàng)是能發(fā)送bool值的通道
for i := range channels { // 通過(guò)`range`初始化切片
channels[i] = make(chan bool)
}
go func() { // 在其他gouroutine中執(zhí)行匿名函數(shù)
for {
channels[rand.Intn(6)] <- true // rand.Intn(n int)的用途是產(chǎn)生一個(gè)不大于n的隨機(jī)數(shù)
} // 發(fā)送數(shù)據(jù)到隨機(jī)出現(xiàn)的通道
}()
for i := 0; i < 36; i++ {
var x int
select { // select 語(yǔ)句當(dāng)監(jiān)聽(tīng)到哪個(gè)分支的同道未阻塞時(shí)就跳轉(zhuǎn)到哪個(gè)分支
case <-channels[0]:
x = 1
case <-channels[1]:
x = 2
case <-channels[2]:
x = 3
case <-channels[3]:
x = 4
case <-channels[4]:
x = 5
case <-channels[5]:
x = 6
}
fmt.Printf("%d ", x)
}
fmt.Println()
}
通過(guò)以上注釋可以很清晰的看到整個(gè)代碼的執(zhí)行流程,,下面我們執(zhí)行代碼:
$ go run goroutine_channel_t.go
6 4 6 6 2 1 2 3 5 1 3 2 1 6 5 3 4 6 6 3 6 1 3 5 4 2 2 5 1 4 2 1 6 6 4 3
defer
, panic
和recover
defer
開(kāi)發(fā)程序時(shí),,有的時(shí)候忘記關(guān)閉打開(kāi)的文件導(dǎo)致程序執(zhí)行失敗,在python中可以很方便的使用with
語(yǔ)句對(duì)這些資源進(jìn)行自動(dòng)管理,。在Go中我們可以使用defer
語(yǔ)句完成這項(xiàng)任務(wù),。defer
語(yǔ)句用于延遲執(zhí)行一個(gè)函數(shù)或者方法或者是當(dāng)前創(chuàng)建的匿名函數(shù),它會(huì)在外部函數(shù)或者方法返回之前但是其返回值計(jì)算之后執(zhí)行,。這樣就可能在一個(gè)延遲執(zhí)行的函數(shù)中修改函數(shù)的命名返回值,。如果一個(gè)函數(shù)中又多個(gè)defer
語(yǔ)句,它們會(huì)以后進(jìn)先出的順序執(zhí)行,。defer
最常用的地方就是保證一個(gè)使用完成后的文件正常關(guān)閉,。如下例子:
var file *os.File
var err error
if file, err = os.Open(filename); err != ni {
do_something(file)
return
}
defer file.Close()
panic
和recover
panic
類(lèi)似于其他程序中的異常,而recover
則用于恢復(fù)異常,。當(dāng)panic()
函數(shù)被調(diào)用時(shí),,外圍函數(shù)或者方法的執(zhí)行會(huì)立即終止。然后任何延遲執(zhí)行的函數(shù)都會(huì)被調(diào)用,。這個(gè)過(guò)程一直在調(diào)用棧中層層發(fā)生,,最后到達(dá)main
函數(shù),這個(gè)時(shí)候整個(gè)程序會(huì)終止,,最終將最初的調(diào)用棧信息輸出到stderr,。但是當(dāng)延遲執(zhí)行函數(shù)中包含recover
語(yǔ)句時(shí),recover
會(huì)捕捉到panic
引發(fā)的異常,,并停止panic
的傳播,,這個(gè)時(shí)候我們能夠以任何我們想用的方式處理panic
,。
Go語(yǔ)言將錯(cuò)誤和異常兩者區(qū)分對(duì)待。錯(cuò)誤是指有可能出錯(cuò)的東西,,程序中已經(jīng)包含處理這些錯(cuò)誤的優(yōu)雅邏輯,。而異常則是指不可能發(fā)生的事情。例如,,一個(gè)永遠(yuǎn)為true的條件在實(shí)際環(huán)境中卻是false,。Go語(yǔ)言推薦使用錯(cuò)誤,而不使用異常,。通常情況下,,我們可以在recover
中阻止panic
的傳播,并將recover()
的返回值轉(zhuǎn)換成錯(cuò)誤,。
使用VIM創(chuàng)建源文件panic_t.go
, 輸入以下代碼:
package main
import (
"fmt"
"math"
)
func ContvertIntToInt16(x int) int16 {
if math.MinInt16 <= x && x <= math.MaxInt16 {
return int16(x)
}
panic(fmt.Sprintf("%d is out of int16 range", x)) // 手動(dòng)觸發(fā)panic
}
func main() {
i := ContvertIntToInt16(655567)
fmt.Printf("%d", i)
}
上面代碼中為了演示panic
,,代碼中手動(dòng)促發(fā)了panic()
的執(zhí)行,但是我們沒(méi)有使用recover
進(jìn)行捕捉,,這會(huì)導(dǎo)致整個(gè)程序執(zhí)行失敗,,下面執(zhí)行程序驗(yàn)證下:
$ go run panic_t.go
panic: 655567 is out of int16 range
goroutine 16 [running]:
runtime.panic(0x96bc0, 0x208178180)
/usr/local/go/src/pkg/runtime/panic.c:279 +0xf5
main.ContvertIntToInt16(0xa00cf, 0x3ec8f)
/Users/aiden/Project/golang/panic_t.go:13 +0x10f
main.main()
/Users/aiden/Project/golang/panic_t.go:17 +0x26
goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
/usr/local/go/src/pkg/runtime/mheap.c:507
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445
goroutine 18 [runnable]:
bgsweep()
/usr/local/go/src/pkg/runtime/mgc0.c:1976
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445
goroutine 19 [runnable]:
runfinq()
/usr/local/go/src/pkg/runtime/mgc0.c:2606
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445
exit status 2
可以看到?jīng)]有捕捉panic
時(shí),整個(gè)程序退出,,并且打印出了調(diào)用棧的異常信息,。
下面我們使用Go語(yǔ)言推薦的做法捕捉panic
并將panic
轉(zhuǎn)換為error, 創(chuàng)建源文件panic_t1.go
,輸入以下代碼:
package main
import (
"fmt"
"math"
)
func ContvertIntToInt16(x int) int16 {
if math.MinInt16 <= x && x <= math.MaxInt16 {
return int16(x)
}
panic(fmt.Sprintf("%d is out of int16 range", x)) // 手動(dòng)觸發(fā)panic
}
func Int16FromInt(x int) (i int16, err error) {
defer func() { // 延遲執(zhí)行匿名函數(shù),,并使用recover捕捉了panic,,并將panic轉(zhuǎn)換為了error
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
i = ContvertIntToInt16(x)
return i, nil
}
func main() {
if _, e := Int16FromInt(655567); e != nil {
fmt.Printf("%v\n", e)
} else {
fmt.Printf("no errors\n")
}
}
以上代碼中,我們通過(guò)recover
捕捉了異常,,現(xiàn)在程序?qū)惓^D(zhuǎn)換成了錯(cuò)誤,所以程序不會(huì)異常退出,,執(zhí)行驗(yàn)證如下:
$ go run panic_t1.go
655567 is out of int16 range
值得注意的地方是,,在以上代碼中的Int16FromInt(x int) (i int16, err error)
函數(shù)中,我們?cè)?code>defer語(yǔ)句的匿名函數(shù)中修改了命名的返回值err
,。該函數(shù)在被調(diào)用時(shí),,Go語(yǔ)言會(huì)自動(dòng)的將其返回值設(shè)置為對(duì)應(yīng)類(lèi)型的零值,在Int16FromInt
函數(shù)中,,i被初始化為0,,err被初始化為nil。當(dāng)在defer
語(yǔ)句中匿名函數(shù)執(zhí)行時(shí)候,,recover
如果捕捉到異常,,然后修改了命名返回值err
,并保持i
的值(零值 )不變,。如果沒(méi)有捕捉到異常,,則程序正常返回i
和nil
,。
請(qǐng)使用本節(jié)所講知識(shí)編寫(xiě)一個(gè)函數(shù),實(shí)現(xiàn)輸入一個(gè)正整數(shù)n,,輸出2-n之間的所有素?cái)?shù),。
|
來(lái)自: 心平9bwburua3p > 《待分類(lèi)》