iszjw 发表于 2018-9-20 13:16:30

四、golang内置函数、递归、闭包、数组切片和map

  一、总体内容
  1、内置函数、递归函数、闭包
  2、数组和切片
  3、map数据结构
  4、package介绍
  一、内置函数
  注意:值类型用new来分配内存,引用类型用make来分配内存
  1、close:主要用来关闭channel
  2、len:用来求长度,比如string、array、slice、map、channel
  3、new:用来分配内存,主要用来分配值类型,比如int、struct、浮点型。返回的是指针
代码案例
  

package main  

  
import(
  
"fmt"
  
)
  

  
func main(){
  
var i int
  
fmt.Println(i)//0
  

  
j:=new(int)//返回的是地址,也就是指针
  
*j=100       //因为是指针,所以要这样赋值
  
fmt.Println(*j)   //100
  
}
  

  
4、make:用来分配内存,主要用来分配引用类型。比如chan、map、slice
  
5、append:用来追加元素到数组、slice中
  
package main
  

  
import(
  
"fmt"
  
)
  

  
func main(){
  
var a [] int
  
a=append(a,10,20,30)
  
fmt.Println(a)//
  
}
  
合并两个slice
  
package main
  

  
import(
  
"fmt"
  
)
  

  
func main(){
  
var a [] int
  
a=append(a,10,20,30)
  
a=append(a, a...)//后面的三个点,是展开,这个append是合并
  
fmt.Println(a)
  
}
  
注意这里的三个点的作用是展开
  

  

  6、panic和recover:用来做错误处理
  panic可以快速定位到哪里出错了
  捕获异常的原因是因为上线项目之后不能够随便的停止,所以要捕获
  

package main  

  
import(
  
"fmt"
  
"time"
  
)
  

  
func test(){
  

  
defer func(){
  
if err:=recover();err !=nil{ //这里捕获下面的系统的异常
  
fmt.Println(err)          //这里的错误没有指定哪一行出错了。这里可以把堆栈打印出来
  
//捕获了异常之后下面可以继续写上报警的接口或者写到日志里面
  
}
  
}()
  
b:=0
  
a:=100/b//这里系统抛了一个异常,上面来捕获
  
fmt.Println(a)
  
return
  
}
  

  

  
func main(){
  
for {
  
test()
  
time.Sleep(time.Second)//这里是休息1秒,参数是一个常量
  
}
  

  
var a [] int
  
a=append(a,10,20,30)
  
a=append(a, a...)
  
fmt.Println(a)//
  
}
  
D:\project>go build go_dev/day4/example/example2
  

  
D:\project>example2.exe
  
runtime error: integer divide by zero
  
runtime error: integer divide by zero。
  

  

  还可以自己手动的通过pinic来捕获异常
  

package main  

  
import(
  
"fmt"
  
"errors"
  
)
  

  
func initConfig()(err error){                     //这里是命名返回值
  
return errors.New("init config failed")   //初始化error这个实例
  
}
  

  

  
func test(){
  

  
err :=initConfig()
  
if err !=nil{
  
panic(err)   //panic手动的捕获这个异常,这样就能知道程序的哪里出错了
  
}
  
return
  
}
  

  

  
func main(){
  
test()
  
var a [] int
  
a=append(a,10,20,30)
  
a=append(a, a...)
  
fmt.Println(a)//
  
}
  
D:\project>go build go_dev/day4/example/example2
  

  
D:\project>example2.exe
  
panic: init config failed
  

  
goroutine 1 :
  
main.test()
  
D:/project/src/go_dev/day4/example/example2/main.go:17 +0xb2
  
main.main()
  
D:/project/src/go_dev/day4/example/example2/main.go:24 +0x3b
  

  

  数组:
  var a int:这个是确定的长度,为1
  var a [] int   :这个是切片
  new和make的区别
  new之后如果是slice等引用类型必须要用make初始化一下才可以用如下:
  

package main  

  
import(
  
"fmt"
  
)
  

  
func test(){
  
s1:=new([] int)    //new了这个指针,必须要用make来初始化一下
  
fmt.Println(s1)   //这个返回来一个指针&[]
  

  
s2:=make([] int, 2)//这个后面的2是指定容量
  
fmt.Println(s2)   //这个返回来这个数据类型
  

  
*s1=make([] int,5)//因为s1是一个指针(地址),要想用这个指针必须初始化一下
  
(*s1)=100      //初始化之后给这个值赋值
  
fmt.Println(s1)   //&
  

  
s2=100
  
fmt.Println(s2)    //
  
}
  

  
func main(){
  
test()
  
}
  

  

  递归函数
  一个函数调用自己,就叫做递归
  

package main  
package main
  

  
import (
  
"fmt"
  
"time"
  
)
  

  
func recusive(n int){
  
fmt.Println("hello")
  
time.Sleep(time.Second)
  
if n>10{   //这个是递归退出的条件
  
return
  
}
  
recusive(n+1)    //这个就是递归的自己调用自己
  
}
  

  
func main(){
  
recusive(0)
  
}
  

  

  例子一:计算阶乘

  

package main  

  
import (
  

"fmt"  

  
)
  

  
func factor(n
int) int {  

if n==1{  

return 1  
}
  

return factor(n-1)*n  
}
  

  
func main(){
  
a:
=factor(5)  
fmt.Println(a)
  
}
  
D:\project
>go build go_dev/day4/example/example5  

  
D:\project
>example5.exe  

120  


View Code  斐波那契数列

  

package main  

  
import(
  

"fmt"  
)
  

  
func fabnaqi(n
int) int{  

if nexample6.exe  

1  
2
  
3
  
5
  
8
  
13
  
21
  
34
  
55
  


View Code  闭包:
  闭包:是一个函数和与其相关作用域的结合体
  下面的匿名函数中的变量和x绑定了

  

package main  

  
import (
  

"fmt"  
)
  

  

//闭包  
func Adder() func(int)int{    //这里定义的返回值要和下面的匿名函数一样
  
//下面的整体才是闭包
  
var x int               //默认为0
  
return func(d int) int{   //这里匿名函数要和上面返回值类型是一样
  
x+=d
  
return x
  
}
  
}
  

  
func main(){
  
f:=Adder()
  
fmt.Println(f(1))      //这里的f就是执行Adder(),然后f(1),这个就是执行匿名函数,并且传入参数
  
fmt.Println(f(100))   //101
  
fmt.Println(f(1000))    //1101
  

  
}
  


View Code  例2

  

package main  

  
import (
  

"fmt"  
"strings"
  
)
  

  
func makeSuffixFunc(suffix string) func (string )string{
  
return func (name string) string{   //这里的闭包的环境变量是和suffix绑定的
  
if strings.HasPrefix(name,suffix)==false{
  
return name+suffix
  
}
  
return name
  
}
  
}
  

  
func main(){
  
func1:=makeSuffixFunc(".bmp")
  
func2:=makeSuffixFunc(".jpg")
  
fmt.Println(func1("test"))
  
fmt.Println(func2("test"))
  
}
  


View Code  数组
  1、数组:是同一种数据类型的固定长度的序列
  2、数组定义:var a int 比如:var a int   一旦定义长度就不会变了
  3、长度是数组类型的一部分,因此var aint和var aint 是不同的类型
  4、数组可以通过下标进行访问,下标是从零开始的,最后一个元素下标是len-1
  遍历的方法如下:
  下面是遍历数组的两种方法

  

package main  

  
import (
  

"fmt"  
)
  

  
func main(){
  

var a int  

  
a[
0]=100  

  
for i:=0;iexample15
  

  
*/
  
下面是对字符串进行排序
  
func testString(){
  
var a=[...]string{"abc","efg","b","A","eee"}
  
sort.Strings(a[:])
  
fmt.Println(a)//
  
}
  

  
func main(){
  
testString()
  
}
  
下面是对浮点型进行排序
  
func testFloat(){
  
var a=[...]float64{1.1,9.7,2.1}
  
sort.Float64s(a[:])
  
fmt.Println(a)   //
  
}
  

  
func main(){
  
testFloat()
  
}
  
下面是排序的方法
  
func testIntsearch(){
  
var a=[...]int{1,8,38,2,348,4}
  
sort.Ints(a[:])
  
index:=sort.SearchInts(a[:],2)
  
fmt.Println(index)    //得到下标为1
  
}
  

  
func main(){
  
testIntsearch()
  
}
  


View Code  map数据结构
  一、简介
  key-value的数据结构
  a、声明,声明是不会分配内存的,初始化需要make
  var map1 mapvalue type
  var a mapstring
  var a mapint
  var a mapstring
  var a mapmapstring

  

package main  

  
import (
  

"fmt"  
)
  

  
func test(){
  

var a mapstring//声明map,但是这里没有申请内存空间,所以必须初始化  
a=make(mapstring,10)//这里初始化,申请内存空间
  
a["abc"]="efg"
  
fmt.Println(a)//map
  
}
  

  
func main(){
  
test()
  
}
  
方式二
  
package main
  

  
import (
  
"fmt"
  
)
  

  
func test(){
  
a:=make(mapstring,10)//这里声明加上初始化,声明然后分配内存空间
  
a["abc"]="efg"
  
fmt.Println(a)//map
  
}
  

  
func main(){
  
test()
  
}
  
方法三:不推荐
  
package main
  

  
import (
  
"fmt"
  
)
  

  
func test(){
  
var a mapstring=mapstring{
  
"key":"value",
  
}
  
//var a mapstring//声明map,但是这里没有申请内存空间,所以必须初始化
  
//a:=make(mapstring,10)//这里初始化,申请内存空间
  
a["abc"]="efg"
  
fmt.Println(a)//map
  
}
  

  
func main(){
  
test()
  
}
  
多层map嵌套
  

  
func testMap2(){
  
a:=make(mapmapstring,100)//声明
  
a["key1"]=make(mapstring)//初始化
  
a["key1"]["key2"]="abc"
  
a["key1"]["key3"]="abc"
  
fmt.Println(a)
  
}
  

  
func main(){
  
testMap2()
  
test()
  
}
  


View Code  map相关的操作
  a[“hello”]=”world”   插入和更新
  val,ok:=a[“hello”]    查找
  for k,v:=range a {   遍历
  }
  delete (a,”hello”)   删除
  len(a)            长度

  

func test3() {  

var a mapstring = mapstring{"hello": "world"}  
a
= make(mapstring, 10)  
a[
"hello"] = "world"//插入和更新  
val,ok:=a["hello"]//查找
  
if ok{
  
fmt.Println(val)
  
}
  
for k,v :=range a {   //遍历
  
      fmt.Println(k,v)
  
}
  

  

  
}
  

  
func main(){
  
test3()
  
}
  


View Code  排序:
  map排序,map的排序是无序的:
  a、先获取所有key,把key进行排序
  b、按照排好的key,进行遍历

  

package main  

  
import(
  

"fmt"  
"sort"
  
)
  

  
func test(){
  
var a mapint               //声明
  
a=make(mapint,5)            //初始化,加入内存
  

  
a=10
  
a=11
  
a=10
  
a=10
  
a=10
  

  
var keys []int         //创建一个切片
  
for k,_:=range a{
  
keys=append(keys,k)
  
}
  

  
sort.Ints(keys)
  

  
for _,v:=range keys{
  
fmt.Println(v,a)
  
}
  
}
  

  
func main(){
  
test()
  
}
  

  
//D:\project>go build go_dev/day4/example/example17
  
//
  
//D:\project>example17.exe
  
//1 10
  
//2 10
  
//3 11
  
//8 10
  
//18 10
  
上面思路,由于map是无序的,这里创建一个切片,然后根据切片的方法进行排序
  


View Code  map反转
  初始化另外一个map把key、value呼唤即可
  

package main  

  
import (
  
"fmt"
  
)
  

  
func test(){
  
var a map int
  
var b map string
  

  
a=make(mapint,5)
  
b=make(mapstring,5)
  

  
a["abc"]=1
  
a["efg"]=2
  

  
for k,v:=range a{
  
b=k
  
}
  

  
fmt.Println(a)//map
  
fmt.Println(b)//map
  
}
  

  
func main(){
  
test()
  
}
  
原理:这里首先声明两个map,并且初始化,然后进行遍历第一个map,然后第二个map直接添加第一个map的v和k即可
  

  

  包
  1、golang中的包
  a、golang目前有150个标准的包,覆盖了几乎所有的基础库
  b、golang.org有所有包的文档,没事就翻翻
  2、线程同步
  a、import(“sync”)
  b、互斥锁 var mu sync.Mutex,同一时间只能有一个goroute能进去
  c、读写锁,var mu sync.RWMutex
  线程和协程,只有读操作的时候不用加锁
  1、如果有写操作,需要加锁
  2、如果有读写的操作,需要加锁
  3、如果只有读的操作,不需要加锁
  编译的时候—race可以查看是否有竞争
  下面是互斥锁的程序

  

import (  

"fmt"  
"sync"
  
"math/rand"
  
"time"
  
)
  

  
//因为这是一个读写的操作,所以读的时候也要加锁
  
var lock sync.Mutex
  
func test(){
  
var a map int
  
a=make(mapint,5)
  

  
a=10
  

  
for i :=0;igo build --race go_dev/day4/example/packagea   这里—race是检测是否有竞争
  

  
D:\project>packagea.exe                                 //因为有race所以下面检测没有竞争
  
map
  
上面是互斥锁
  
应用场景:
  
写比较多,有少量的读。(写多读少)
  
加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁
  
如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态,
  
第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。
  
在这种方式下,只有一个线程能够访问被互斥锁保护的资源。
  
互斥锁无论读还是写同一时间只有一个协程在执行
  


View Code  读写锁:
  实际就是一种特殊的自旋锁,它把共享资源的访问者划分为读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作
  使用场景:
  写比较少,有大量的读 (读多写少)
  写的时候只有一个goroute去写,但是读的时候所有的goroute去读
  读写锁有三种状态:读加锁状态、写加锁状态和不加锁状态
  
一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。(这也是它能够实现高并发的一种手段)
  
当读写锁在写加锁模式下,任何试图对这个锁进行加锁的线程都会被阻塞,直到写进程对其解锁。
  
当读写锁在读加锁模式先,任何线程都可以对其进行读加锁操作,但是所有试图进行写加锁操作的线程都会被阻塞,直到所有的读线程都解锁。
  
所以读写锁非常适合对数据结构读的次数远远大于写的情况

  

package main  

  
import (
  

"fmt"  
"sync"
  
"math/rand"
  
"time"
  
)
  

  
var rwlock sync.RWMutex   //读写锁的定义
  

  
func test(){
  
var a map int
  
a=make(mapint,5)
  
//var count int32
  

  
a=10
  

  
for i:=0;i
页: [1]
查看完整版本: 四、golang内置函数、递归、闭包、数组切片和map