Go語言中的程序結構

寫在前面

本篇文章主要介紹程序結構相關的知識,具體包括條件語句、循環、函數和指針等內容。編程

條件語句

if語句

給定一個天然數v,若是它在0-100之間則返回v,若大於100則返回100,小於0則返回0,使用Go語言實現的代碼以下:ide

package main

import "fmt"

func ifTest(v int) int{
	if v >100 {   //if的條件裏面不須要括號
		return 100
	}else if v <0 {
		return 0
	}else {
		return v
	}
}

func main() {
	var a int = ifTest(5)
	fmt.Println(a)
}

//運行結果:
5

注意到沒有if的條件裏面不須要括號,建立的文件中不能包含下劃線。func ifTest(v int) int中參數v的前面不須要添加var關鍵詞,後面的int則是該函數的返回結果。函數式編程

如今來使用Go語言來讀取某個文件的信息,如test.txt中的內容,相應的代碼以下:函數

package main

import (
	"fmt"
	"io/ioutil"
)

func readFieTest(){
	const filename  =  "test.txt"
	contents, errorinfo :=ioutil.ReadFile(filename)   
	//var contents ,errorinfo = ioutil.ReadFile(filename)
	if errorinfo != nil {   
		fmt.Println(errorinfo)
	}else{
		fmt.Printf("%s\n",contents)
	}
}


func main() {
	readFieTest()
}

讀取文件須要採用ioutil包中的ReadFile函數,查看源碼可知該函數一次能夠返回兩個值:ui

func ReadFile(filename string) ([]byte, error) {
......
}

if errorinfo != nil中的nil就是無的意思,此處就是產生了錯誤,能夠參考這篇文章瞭解更多關於nil的信息:理解Go語言的nil 。其實上面那種方式不是很簡介,可使用相似於Java中的三元表達式:.net

package main

import (
	"fmt"
	"io/ioutil"
)

func readFieTest(){
	const filename  =  "test.txt"
	if contents, errorinfo := ioutil.ReadFile(filename) ;errorinfo != nil {  
//先運行前半句後進行判斷
		fmt.Println(errorinfo)
	}else {
		fmt.Printf("%s",contents)
	}
}

func main() {
	readFieTest()
}

發現沒有if的條件裏能夠進行賦值,且if條件裏賦值的變量做用域就是這個if語句。接下來聊一聊switch,不少語言中都有switch。指針

switch語句

switch後面是能夠接表達式的(也能夠不接),使用Go實現計算某兩個整數的加減乘除的功能,相應的代碼以下:code

package main

import "fmt"

func eval(a, b int,operation string) int {
	var result int
	switch operation {
	case "+":
		result = a +b
	case "-":
		result = a-b
	case "*":
		result = a*b
	case "/":
		result = a/b
	default:
		panic("不支持的運算方式"+operation)   //這個panic就是報錯,讓程序停下來
	}
	return result
}

func main() {
	test:= eval(3,4,"*")
	fmt.Println(test)
}


//運行結果:
12

細心的你發現什麼奇特之處麼?對,裏面沒有break,由於Go語言中的switch會自動break,除非使用了fallthrough。而在C、C++或者是Java中你要麼在後面添加break要麼添加continue。對象

再來舉一個例子,用於判斷學生成績狀況:當分數小於60,則顯示不及格;60-69爲及格;70-79爲中等;80-89爲良好;90-100爲優秀。使用Go語言實現的代碼以下:blog

package main

import "fmt"

func scoreTest(score int)string {
	var result string = ""
	switch {
	case score <0 ||score >100 :
		panic(fmt.Sprintf("無效的分數:%d",score))   //若是這個條件成立,則程序再也不往下執行
	case score <60:
		result = "不及格"
	case score<70:
		result = "及格"
	case score <80:
		result = "中等"
	case score <90:
		result = "良好"
	case score <=100:
		result = "優秀"
	}
	return result
}

func main() {
	fmt.Println(
		scoreTest(59),
	    scoreTest(62),
		scoreTest(77),
		scoreTest(84),
		scoreTest(99),
		//scoreTest(-99),
		)
}

//運行結果:
不及格 及格 中等 良好 優秀

若是程序知足panic的要求,則程序會中止運行。switch後面能夠沒有表達式

循環語句

for語句

使用Go語言實現求解0-指定數字內的數字之和,如100之內整數的和,相應的代碼以下:

package main

import "fmt"

func sumTest(a int) int {
	sum := 0
	for i :=0;i<=a;i++ {
		sum+=i
	}
	return sum
}
func main() {
	fmt.Println(sumTest(100))
}

上面使用了for循環,能夠發現這個for循環的格式除了條件中不包含括號之外,其實和Java,JavaScript的代碼徹底一致。且你們要學會在函數中儘可能使用:=的方式替代var來聲明變量。

for的條件中不包含括號,且條件中可省略初始條件,結束條件以及遞增表達式

再來看一個例子,將整數轉換成二進制的表達式,相應的代碼以下:

package main

import (
	"fmt"
	"strconv"
)

func intToBinary(n int)string {
	result := ""
	for ;n>0;n/=2{   //省略初始條件,至關於while
		lsb := n%2
		result = strconv.Itoa(lsb) +result
	}
	return result
}

func main() {
	fmt.Println(
		intToBinary(5), // 101
		intToBinary(13), //1101
		intToBinary(121242),
		)
}

再來換一種方式讀取以前那個test.txt文件中的內容,如今是一行行的進行讀取:

//一行行讀取
func printFileTest(filename string){
	file, err :=os.Open(filename)
	if err != nil{
		panic(err)  //程序停下來去報錯
	}else{
		scanner := bufio.NewScanner(file)
		for scanner.Scan(){   
 // 這裏既沒有開始條件,也沒有遞增條件,只有結束條件,此時分號均可以不寫,Go語言中沒有while
		    fmt.Println(scanner.Text())  //輸出
		}
	}
}

func main() {
	printFileTest("test.txt")
}

在這段代碼裏面for中既沒有開始條件,也沒有遞增條件,只有結束條件,那麼此時的分號均可以不寫,記住Go語言中沒有while。由於while的功能和for類似,因此Go語言中就沒有必要存在while這個關鍵詞了。

當for中什麼也不加,則變成了一個死循環,就至關於其餘語言中的while true。Go語言中的死循環實現起來很是簡單,那是由於後面會常用到死循環。

簡單總結一下循環語句的特色:一、for和if條件後面沒有括號;二、if條件裏面也能夠定義變量;三、Go語言中沒有while;四、switch中不須要定義break,也能夠直接switch多個語句。

函數

其實在前面咱們就使用了func這個關鍵詞用於定義函數,函數定義的格式爲:

func 函數名稱(參數名稱,參數類型)返回值類型{
......
}

須要說明的是,Go語言的函數能夠有多個返回值的,且類型能夠不相同:

package main

import "fmt"

//求解兩個數的和
func sumTest(a,b int)int{
	return a+b
}

//求兩個數相除的商及餘數
func divTest(a ,b int) (int,int, string) {
	return a/b, a%b, "你好"
}

func main() {
	fmt.Println(sumTest(3,6))
	fmt.Println(divTest(13, 4))
}

//運行結果:
9
3 1 你好

在上面的代碼中不知道返回的究竟是什麼,只知道都是int類型,其實能夠像聲明變量的方式那樣給返回值設置名稱:

//求兩個數相除的商及餘數
func divTest(a ,b int) (q, r int, s string) {
	return a/b, a%b, "你好"
}

因爲Go語言很是嚴格,定義的變量必定要使用,若是函數有多個返回值,咱們只想取某個值時,那麼其他的變量可使用匿名變量_來接收。儘管Go語言支持返回多個類型值,可是不要亂用,通常返回兩個,前者是數據,後者是錯誤nil,以下圖所示。將前面實現兩個數的四則運算的相關代碼進行改寫:

package main

import "fmt"

func calcTest(a, b int,operation string ) (int, error) {
	switch operation {
	case "+":
		return a+b, nil
	case "-":
	    return a-b,nil
	case "*":
        return a*b,nil
	case "/":
        return a/b,nil
	default:
		return 0,fmt.Errorf("不支持的運算操做:%s",operation)
	}

}

func main() {
	fmt.Println(calcTest(3,5,"+"))
}

//運行結果:
8 <nil>

上述代碼其實還不夠完善,在main方法中對正常與否須要進行判斷:

func main() {
	if result ,err:= calcTest(3,5,"+");err != nil{
		//程序運行存在錯誤
		fmt.Println("程序運行存在錯誤",err)
	}else{
		fmt.Println(result)
	}
}

在Go語言中函數能夠返回多個值,且能夠給多個值聲明名稱,可是返回多個值的狀況僅僅適用於很是簡單的函數,不過取不取名字和調用者無關。

Go語言是函數式編程,函數是一等公民(Python中也是),函數裏面的參數,返回值裏面均可以包含函數。經過前面求兩個數的四則運算這個例子進行改寫,實現將函數做爲參數:

 

Go語言沒有其餘語言中的默認參數、可變參數、函數重載等,只有一個可變參數列表:

//求可變參數列表中參數之和
func dynamicVariable(values ... int)int {
	sum :=0
	for i:=range values{
		sum+=values[i]
	}
	return sum
}

func main() {
	fmt.Println(dynamicVariable(1,2,3,4,5,6))
}

//運行結果:
21

函數小結:一、函數返回值的類型寫在最後面;二、函數能夠返回多個值;三、函數可做爲參數進行使用;四、沒有默認參數和可選參數,函數重載等。

指針

你們不要聽到指針就懼怕,Go語言中的指針和C語言中的指針差異很大(Go語言中的指針不能運算,而C語言中卻能夠),比C中的指針簡單多了。

看到這裏就必須談到一個老生常談的問題:Go語言中的參數傳遞是值傳遞仍是引用傳遞?在C和C++中既能夠值傳遞也能夠引用傳遞。Java和Python絕大部分都是引用傳遞,除了系統的自建類型之外。那麼什麼是值傳遞?什麼是引用傳遞呢?咱們經過C++中的一段代碼進行了解(C++中使用&表示引用傳遞):

void pass_by_value(int a){  //值傳遞
a++;
}

void pass_by_guide(int& a){  //引用傳遞
a++;
}

int main(){
int a =3;
pass_by_value(a)
printf("值傳遞之後的值爲:%d\n",a);

pass_by_guide(a)
printf("引用傳遞之後的值爲:%d\n",a);
}

//運行結果:
3  4

pass_by_value是值傳遞,會將a的值從main函數中拷貝一份到pass_by_value函數中,真正做了一份拷貝,拷貝進去的a加了1,那麼main函數中的a並無發生變化,沒有動依舊是3。pass_by_guide是引用傳遞,它不會拷貝,此時main函數中的a和pass_by_guide中的a實際上是引用了同一個變量a,所以在pass_by_guide函數中進行了加1操做,天然main函數中的a也會發生變化,所以就變成了4。值傳遞就是拷貝,原來值不會發生變化;引用傳遞不會拷貝,會致使原來的值發生變化。

**Go語言只有值傳遞一種方式。**Go語言中的參數須要配合其指針來使用,具體分狀況:

上面這種就是值傳遞,二者沒有影響。下面是使用到了指針的狀況:

左側是一個int類型名爲a的變量,右側是一個int類型名爲aa的指針,經過指針實現至關於引用傳遞的效果,把a的地址給了你之後,能夠修改a的值。這些都是基本數據類型,再來嘗試一個自定義類型:

當把左側的book對象傳給右側的read函數時,通常這個book對象自己一般包含指向data的一個指針,而後拷貝一份到右側函數中,右側的book對象也有一個pdata,可是是指向同一個data,其實就是拷貝了同一份指針。所以在Go語言中,自定義類型的時候須要考慮把它做爲一個值仍是一個指針來用。這裏的book其實就是做爲一個值來用。

用一個交換兩個對象的值這個例子來加深你們的印象:

//交換兩個對象的值
func swap(a,b int) {
	a,b = b ,a
}

func main() {
	a ,b := 1,2
	swap(a,b)
	fmt.Println(a, b)
}

//運行結果:
1,2

你會發現這個函數沒有用,兩個數值並無發生交換,的確是這樣的,那是由於這個須要藉助於指針來完成:

//交換兩個對象的值
func swap(a,b *int) {
	*a,*b = *b ,*a  //聲明指針須要使用*
}

func main() {
	a ,b := 1,2
	swap(&a,&b)  //傳遞地址須要使用&
	fmt.Println(a, b)
}

//運行結果:
2,1

不過這種看起來挺麻煩的,其實以前的代碼不是沒有起做用,而是沒有將結果進行返回,修改一下代碼實際上是能夠的:

func swapTest(a,b int)(int ,int) {
	return b ,a
}


func main() {
	a ,b := 1,2
	a, b = swapTest(a,b)
	fmt.Println(a, b)
}

//運行結果:
2,1

這樣就經過接收函數的返回值,進而實現交換兩個數值的目的。至此程序結構部分就介紹到這裏,後續內容不發了,賊累,去我博客上看!

相關文章
相關標籤/搜索