以下是记录我学习Go时练习的小程序段,接上篇,已经过编译通过,在此记录,以供翻阅复习
这篇知识比之前的要稍微深入一些,要更重视一些才好
//GO Map集合 创建和使用map
/*
Map是一种无序的键值对的集合。通过key 来快速检索数据,key类似于索引,指向数据的值。
对于Map可以像迭代数组和切片那样迭代它。不过Map是无序的,无法决定他的返回顺序,这是因为map是使用hash表来实现的。
定义map
可以使用内建函数make也可以使用map关键字来定义Map
--声明变量,默认map是nil
var map_variable map[key_data_type]value_data_type
--使用make函数
map_variable :=make(map[key_data_type]value_data_type)
如果不初始化map,那么就会创建一个nil map.nil map不能用来存放键值对
*/
package main
import "fmt"
func main(){
var countryCapitalMap map[string]string //创建集合
countryCapitalMap =make(map[string]string) //初始化
/*map插入key-value对*/
countryCapitalMap["France"]="paris"
countryCapitalMap["Italy"]="罗马"
countryCapitalMap["Japan"]="东京"
countryCapitalMap["India"]="新德里"
/*使用键输出地图值*/
for country :=range countryCapitalMap{
fmt.Println(country,"首都是",countryCapitalMap[country])
}
/*查看元素在集合中是否存在*/
capital,ok :=countryCapitalMap["美国"]
fmt.Println(capital)
fmt.Println(ok) //true或者false
if(ok){
fmt.Println("美国的首都是",capital)
}else{
fmt.Println("美国的首都不存在")
}
}
/*运行结果:
France 首都是 paris
Italy 首都是 罗马
Japan 首都是 东京
India 首都是 新德里
false
美国的首都不存在
*/
//delete函数
/*用于删除集合的元素,参数为map和其对应的值key*/
package main
import "fmt"
func main(){
//创建map
countryCapitalMap :=map[string]string{"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New delhi"}
fmt.Println("原始地图")
//打印地图
for country :=range countryCapitalMap{
fmt.Println(country,"首都是",countryCapitalMap[country])
}
//删除元素
delete(countryCapitalMap,"France")
fmt.Println("法国条目被删除")
fmt.Println("删除元素后的地图")
for country:=range countryCapitalMap{
fmt.Println(country,"首都是",countryCapitalMap[country])
}
}
/*
运行结果:
原始地图
Japan 首都是 Tokyo
India 首都是 New delhi
France 首都是 Paris
Italy 首都是 Rome
法国条目被删除
删除元素后的地图
India 首都是 New delhi
Italy 首都是 Rome
Japan 首都是 Tokyo
*/
//Go 递归函数
//递归,就是在运行的过程中调用自己 需要设置退出条件
//使用:计算阶乘 斐波那契数列 等数学问题
/*
func recursion(){
rescursion() 函数调用自身
}
func main(){
recursion()
}
*/
//阶乘
package main
import "fmt"
func Factorial(n uint64)(result uint64){
if(n>0){
result=n*Factorial(n-1)
return result
}
return 1
}
func main(){
var i int=15
fmt.Printf("%d的阶乘是%d\n",i,Factorial(uint64(i)))
}
/*
15的阶乘是1307674368000
*/
//斐波那契数列
//通过Go语言的递归函数来实现斐波那契数列
package main
import "fmt"
func fibonacci(n int)int{
if n<2{
return n
}
return fibonacci(n-1)+fibonacci(n-2)
}
func main(){
var i int
for i=0;i<10;i++{
fmt.Printf("%d\t",fibonacci(i))
}
}
/*
0 1 1 2 3 5 8 13 21 34
*/
//Go 类型转换
/*
类型转换用于将一种数据类型的变量转换为另外一种类型的变量。
type_name(expression)
*/
package main
import "fmt"
func main(){
var sum int=17
var count int=5
var mean float32
mean=float32(sum)/float32(count)
fmt.Printf("mean的值为:%f\n",mean)
}
//mean的值为:3.400000
//Go 接口
//把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口
/*
--定义接口
type interface_name interface{
method_name1[return_type]
method_name2[return_type]
...
method_namen[return_type]
}
--定义结构体
tyoe struct_name struct{
variables
}
--实现接口方法
func (struct_name_variable struct_name) method_name1()[return_type]{
方法实现
}
...
func (struct_name_variable struct_name) method_namen()[return_type]{
方法实现
}
*/
package main
import "fmt"
type Phone interface{
call()
}
type Huawei struct{
}
func (huawei Huawei) call(){
fmt.Println("I am Huawei,T can call you")
}
type IPhone struct{
}
func (iphone IPhone) call(){
fmt.Println("I am IPhone,T can call you")
}
func main(){
var phone Phone
phone=new(Huawei)
phone.call()
phone=new(IPhone)
phone.call()
}
/*
I am Huawei,T can call you
I am IPhone,T can call you
*/
package main
import "fmt"
type Man interface{
name() string
age() int
}
type Woman struct{
}
func (woman Woman) name() string{
return "Jim Yawei"
}
func (woman Woman) age() int{
return 23
}
type Men struct{
}
func (men Men) name() string{
return "liwen"
}
func (men Men) age() int{
return 27
}
func main(){
var man Man
man=new (Men)
fmt.Println(man.name())
fmt.Println(man.age())
man=new (Woman)
fmt.Println(man.name())
fmt.Println(man.age())
}
/*
liwen
27
Jim Yawei
23
*/
//Go 错误处理
//内置的错误接口
/*
type err interface{
Error() string //有返回值的方法
}
通过error接口类型来生成错误信息
通常在最后的返回值中返回错误类型 如errors.New("")
func Sqrt(f float64)(float64,error){
if f<0{
return 0,errors.New("math:square root of negative number")
}
实现
}
--调用
result,error:=Sqrt(-1)
if error!=nil{ --与nil比较
fmt.Println(error)
}
*/
package main
import "fmt"
type DivideError struct{
dividee int
divider int
}
//实现error接口
func (de *DivideError) Error() string{
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat,de.dividee) //传入dividee的值
}
//定义int类型除法运算的函数
func Divide(varDividee int,varDivider int)(result int,errorMsg string){
if varDivider==0{
dData:=DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg=dData.Error()
return
}else{
return varDividee/varDivider,""
}
}
func main(){
//正常情况
if result,errorMsg:=Divide(100,10)
errorMsg==""{
fmt.Println("100/10= ",result)
}
//当被除数为零的时候会返回错误信息
if _,errorMsg :=Divide(100,0)
errorMsg!=""{
fmt.Println("errorMsg is: ",errorMsg)
}
}
/*
运行结果:
100/10= 10
errorMsg is:
Cannot proceed, the divider is zero.
dividee: 100
divider: 0
*/
//panic 与recover
//区别:一个用于主动抛出错误,一个用于捕获panic抛出的错误
/*
两个内置函数:用于处理Go运行时的错误,Panic用于主动抛出错误,recover用于捕获panic抛出的错误
1 引发panic有两种情况:一是程序主动调用,二是程序产生运行时的错误,由运行时检测并退出
2 发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或者运行到最外层函数
3 panic不但可以在函数正常流程中抛出,在defer逻辑里面也可以再次调用panic或抛出panic.defer里面的panic能够被后续执行的defer捕获
4 recover用来捕获panic,阻止panic继续向上传递。recover()和defer一起使用,但是defer只有在后面的函数体内直接被调用才能捕获panic来终止异常,否则返回nil,异常继续向外传递。
使用场景
一般情况下有两种情况用到:
1.程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
2.在调试程序时,通过 panic 来打印堆栈,方便定位错误。
*/
//以下捕获失败
defer recover()
defer fmt.Println(recover)
defer func(){
func(){
recover() //无效,嵌套两层
}()
}()
//以下捕获有效
defer func(){
recover()
}()
func except(){
recover()
}
func test(){
defer except()
panic("runtime error")
}
//多个panic只会捕捉最后一个
package main
import "fmt"
func main(){
defer func(){
if err:=recover()
err!=nil{
fmt.Println(err)
}
}()
defer func(){
panic("three")
}()
defer func(){
panic("two")
}()
panic("one")
}
//Go 并发
//Go语言支持并发,通过go关键字来开启goroutine即可
/*
goroutine是轻量级线程,goroutine的调度是由Golang运行时来管理的
语法格式
---go 函数名(参数列表)
允许使用go语句开启一个新的运行期线程,即goroutine,以一个不同的新创建的goroutine来执行一个函数。
同一个程序中的所有goroutine共享一个地址空间
*/
package main
import "fmt"
import "time"
func say(s string){
for i := 0; i < 6; i++ {
time.Sleep(100*time.Millisecond)
fmt.Println(s)
}
}
func main(){
go say("world") //开启了一个新的线程
say("hello")
}
/*
执行以上代码,
会看到输出的 hello 和 world 是没有固定先后顺序。
因为它们是两个 goroutine 在执行
*/
/*
world
hello
world
hello
world
hello
world
hello
world
hello
*/
//通道 Channel
/*
通道是
用来传递数据的一个数据结构
通道可用于两个goroutine之间 通过传递一个指定类型的值来同步运行和通讯
操作符<-用于指定通道的方向,发送或者接收;未指定方向时,则表示双向通道
声明一个通道使用chan关键字即可。使用前必须先创建
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据 并把值赋给 v
ch :=make(chan int) //使用make 创建通道
默认情况下,通道是不带缓冲区的。
发送端发送数据,必须有相应的接收端来接收数据。
*/
package main
import "fmt"
func sum(s []int,c chan int){
sum:=0
for _,value :=range s{
sum+=value
}
c<-sum //把 sum 发送到通道 c
}
func main(){
s:=[]int{7,2,1,-5,3,6,0}
c:=make(chan int)
go sum(s[ :len(s)/2],c)
go sum(s[len(s)/2: ],c)
x,y := <-c,<-c //从通道c中接收
fmt.Println(x,y,x+y)
}
//4 10 14
//Go 通道缓冲区
/*
通道可以设置缓冲区,通过make 的第二个参数指定缓冲区大小
ch :=make(chan int,100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端的数据可以放到缓冲区里面,可以等待接收端去获取数据,而不是立即需要一个接收端去获取数据
不过由于缓冲区的大小是有限的,还是必须有接收端来接收数据。否则缓冲区一满,数据发送端无法再继续发送数据。
注意:
如果通道不带缓冲,发送方会阻塞 直到接收方从通道中接收了值
如果带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区中;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接受方在有值可以接受之前会一直阻塞。
*/
package main
import "fmt"
func main(){
//这里我们定义了一个存储整数型类型的带缓冲通道
ch :=make(chan int,2)
//因为ch是带缓冲的通道,我们可以同时发送两个数据,而不用立刻需要去同步读取数据
ch<-1
ch<-2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
/*运行结果
1
2
*/
//GO 遍历通道 与 关闭通道
/*
Go通过range关键字来实现遍历读取到的数据,类似于切片或数组
v,ok :=<-ch
如果通道接收不到数据则ok的值为false,这是就可以使用close()函数来关闭
*/
package main
import "fmt"
func fibonacci (n int,c chan int){
x,y :=0,1
for i := 0; i < n; i++ {
c <- x //把数据存到缓冲区
x,y =y,x+y
}
close(c)
}
func main(){
c :=make(chan int,10)
go fibonacci(cap(c),c)
/*range 函数遍历每个从通道接收到的数据,
因为 c 在发送完 10 个数据之后就关闭了通道,
所以这里我们 range 函数在接收到 10 个数据之后就结束了。
如果上面的 c 通道不关闭,那么 range 函数就不会结束,
从而在接收第 11 个数据的时候就阻塞了。*/
for i :=range c{
fmt.Println(i)
}
}
/*
运行结果:
0
1
1
2
3
5
8
13
21
34
*/