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

这样就经过接收函数的返回值,进而实现交换两个数值的目的。至此程序结构部分就介绍到这里,后续内容不发了,贼累,去我博客上看!

相关文章
相关标签/搜索