【Go】Go语言学习笔记--Go语言基础【下】

简介: 运算符Go语言内置的运算符:算术运算符关系运算符逻辑运算符位运算符赋值运算符

值为切片的map

var sliceM1 = make(map[string][]string, 4)
sliceM1["names"] = make([]string, 4)
sliceM1["names"] = append(sliceM1["names"], "小王","小黄","小李")
fmt.Println(sliceM1)
复制代码

练习题

  1. 写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。
// 我的解法
var words string = "how do you do"
var wordsSlice []string = strings.Split(words, " ")
var wordCountMap map[string]int = make(map[string]int)
for _, word := range wordsSlice{
   var _, isIn = wordCountMap[word]
   if isIn {
      wordCountMap[word]++
   } else {
      wordCountMap[word] = 1
   }
}
var printMsg = "在"how do you do"中"
for key,val := range wordCountMap {
   str:= key + "=" + strconv.Itoa(val) + ";"
   printMsg += str
}
fmt.Print(printMsg)
复制代码
  1. 观察下面代码,写出最终的打印结果。
type Map map[string][]int
m := make(Map) //[]
s := []int{1,2} // [1 2] len 2 cap 2 x001
s = append(s,3) [1 2 3] len 3 cap 4 x002
fmt.Printf("%+v \n", s) //[1 2 3] len3 cap4 x002
m["q1mi"] = s // [q1mi: [1 2 3] len3 cap4 x002]
s = append(s[:1], s[2:]...) //[1 3] len 2 cap4 x002
fmt.Printf("%+v\n",s) //len2 cap4 x002 [1 3]
fmt.Printf("%+v\n",m["q1mi"])// len3 cap4 //[1 3 3] x002
[1 2 3] [1 3] [1 3 3]
复制代码

函数

Go语言中支持函数、匿名函数和闭包。

函数定义

格式如下:

func 函数名(参数)(返回值){
    函数体
}
复制代码

举个🌰

func main(){
    fmt.Println(intSum(1,2)) //3
}
func intSum(x int, y int)(int) {
    return x + y
}
复制代码

参数

类型简写

函数参数中相邻变量的类型相同,则可以省略类型,举个🌰

func intSum(x,y int) int {
    return x + y
}
复制代码

可变参数

指函数的参数数量不固定,通过在参数名后面加...来表示。

注意:可变参数通常要作为函数的最后一个参数,举个🌰

func intSum(x ...int) int {
    sum := 0
    for _,v := range x {
        x += sum
    }
    return sum
}
复制代码

也可以与固定参数搭配使用

func intSum(x int, y ...int) int {
    sum := x
    for _,v := range x {
        x += sum
    }
    return sum
}
复制代码

本质上,函数的可变参数是通过切片来实现的。

返回值

Go语言通过return关键字向外输出返回值,这一点与JavaScript相同,但也有不同点。

多个返回值

func calc(x, y int) (int, int) {
    sum := x + y
    sub := x - y
    return sub, sum
}
复制代码

返回值命名

函数在定义时可以给返回值命名,并在函数体重使用这些变量,最后用return返回

func calc(x, y int) (sub, sum int) {
    sum = x + y
    sub = x - y
    return
}
复制代码

返回值补充

当函数返回值类型为slice是,nil可以看做是一个有效的slice,没必要返回一个长度为0的切片。

func someFunc(x string) []int {
    if x == "" {
        return nil
    } else {
        res := make([]int,4)
        return res
    }
}
复制代码

变量作用域

全局变量

全局变量指的是定义在函数外部的变量,程序运行期间一直都可以有效访问,与JavaScript的全局变量类似。

var commonNum int = 100 // 全局变量 commonNum
func main() {
    fmt.Println("num:", num) //100
}
func testCommonVar() {
    fmt.Println("num": num) //100
}
复制代码

局部变量

如果全局变量与局部变量重名,那么优先访问局部变量。

var num = 1
func main() {
 num := 10
 fmt.Println(num) // 10
}
复制代码

语句块定义的变量(类似JavaScript中的块级作用域),比如forif等,举个🌰

func testLocalVar(x, y int) {
    fmt.Println(x,y)// 函数的参数也只在函数内生效
    if x>0 {
      z := 10
      fmt.Println(z)// z只能在此处生效
    }
    fmt.Println(z) //不生效
}
复制代码

函数类型与变量

定义函数类型

可以使用type关键字定义一个函数的类型,格式如下:

type calculation func(int, int) int
复制代码

上面语句定义了一个calculation类型,是一种函数类型,这种函数接受两个int类型的参数并返回一个int类型的返回值。

也就是说,凡是满足这个条件的函数,都是calculation类型的函数,举个🌰

func add(x, y int) int {
    return x + y
}
func sub(x, y int) int {
    return x - y
}
复制代码

add和sub都能赋值给calculation类型的变量。

var c calculation
c = add
c(3,4) //7
复制代码

函数类型变量

可以声明函数类型的变量并且为该变量赋值

var c calculation
c = add
fmt.Printf("type of c: %T\n", c) // main.calculation
fmt.Println(c(1,2))
f := add
fmt.Printf("type of f: %T\n", f) // func(int, int) int
fmt.Println(c(2,3))
复制代码

高阶函数

高阶函数分为函数作为参数函数作为返回值两部分

函数作为参数

func add(x, y int) int {
    return x+y
}
func calc(x, y int, callback func(int, int) int) int {
    return callback(10, 20)
}
func main() {
    res := calc(10, 20, add)
    fmt.Println(res) //30
}
复制代码

函数作为返回值

func do(s string) (func(int, int) int, error) {
   switch s {
   case "+":
      return add, nil
   case "-":
      return sub, nil
   default:
      err := errors.New("无法识别的操作符")
      return nil, err
   }
}
复制代码

匿名函数和闭包

匿名函数

格式如下:

func(参数)(返回值){
    函数体
}
复制代码

匿名函数因为没有函数名,所以不能直接调用,需要保存到某个变量里,或者作为立即执行函数。

add := func(x, y) int {
    return x + y
}
add(10, 20)
func(x, y) {
    fmt.Println(x + y)
}(1, 2)
复制代码

匿名函数多用于实现回调函数或者闭包

闭包

闭包值得是一个函数与其相关的引用环境组合而成的实体。简单来说就是:闭包=函数+引用环境,举个🌰

func add() func(int) int {
   var x int
   return func(y int) int {
       x += y
       return x
   }
}
f := add()
fmt.Println(f(10))//10
fmt.Println(f(20))//30
f1 := add()
fmt.Println(f1(20))//20
fmt.Println(f1(40))//60
复制代码

与JavaScript相似,函数体内的变量如果被函数体以外的环境引用,就会形成闭包,该变量不会销毁。

闭包示例2:

func makeSuffixFunc(suffix string) func(string) string {
    return func(fileName string) string {
        if !strings.HasSuffix(fileName, suffix) {
            return fileName + suffix
        }
        return fileName
    }
}
复制代码

闭包示例3:

func calc(base int) (func(int) int, func(int) int) {
    add := func(num int) int {
        base += num
        return base
    }
    sub := func(num int) int {
        base -= num
        return base
    }
    return add, sub
}
add, sub := calc(10)
fmt.Println(add(1)) // 11
fmt.Println(add(2)) // 9
复制代码

defer语句

defer语句会将其后面跟孙的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先defer的后执行,后defer的先执行,举个🌰

fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println(4)
fmt.Println("end")
// start 4 end 3 2 1
复制代码

多用于处理资源释放问题,例如:资源清理文件关闭解锁记录时间等。

defer的执行时机

返回值=x --> 运行defer --> ret指令

牢记defer的三条规则

  1. 延迟函数的参数在defer语句出现的时候就已经确定下来了
  2. 后进先出的规则
  3. 延迟函数可能会影响主函数的具名返回值

defer案例

// 主函数拥有匿名返回值,返回变量
//一个主函数拥有一个匿名的返回值,返回使用本地或全局变量,这种情况下defer语句可以引用到返回值,但不会改变返回值。
// 返回5
func f1() int {
  x := 5
  defer func() {
    x++
  }()
  return x 
}
// 主函数拥有具名返回值
// 主函声明语句中带名字的返回值,会被初始化成一个局部变量,函数内部可以像使用局部变量一样使用该返回值。如果defer语句操作该返回值,可能会改变返回结果。
// x =5
// x++
// return
func f2() (x int) {
  defer func() {
    x++
  }()
  return 5
}
// 主函数拥有具名返回值
// x = 5
// y = x
// x++
// return
func f3() (y int) {
  x := 5
  defer func() {
    x++
  }()
  return x
}
// 主函数拥有具名返回值,但是延迟函数的参数从defer开始就确定了,相当于拷贝一份,所以后面的x++是自执行函数x的++
func f4() (x int) {
  defer func(x int) {
    x++
  }(x)
  return 5
}
func main() {
  fmt.Println(f1()) // 5
  fmt.Println(f2()) // 6
  fmt.Println(f3()) // 5
  fmt.Println(f4()) // 5
}
复制代码

defer面试题

func calc(index string, a, b int) int {
  ret := a + b
  fmt.Println(index, a, b, ret)
  return ret
}
func main() {
  x := 1
  y := 2
  defer calc("AA", x, calc("A", x, y))
  x = 10
  defer calc("BB", x, calc("B", x, y))
  y = 20
}
// A 1 2 3
// B 10 2 12
// BB 10 12 22
// AA 1 3 4
复制代码

内置函数

函数名 介绍
close 用来关闭channel
len 用来求长度,比如string array slice map channel
new 用来分配内存,主要用来分配值类型,比如int, struct。返回的是指针
make 用来分配内存,主要用来分配引用类型,比如chan map slice
append 用来追加元素到数组中、slice中
panic和recover 用来做错误处理

panic/recover

panic用来处理错误,可以在任何地方引发,但recover只有在defer调用的函数中有效,举个🌰

func funcA() {
    fmt.Println("func A")
}
func funcB() {
    panic("func B error")
}
func funcC() {
    fmt.Println("func C")
}
func main() {
    funcA()
    funcB()
    funC()
}
复制代码

这时候程序就会崩溃,因为funcB中引发了panic,但是,我们可以通过recover让程序起死回生!只需要对funcB做一下修改举个🌰

func funcB() {
    defer func(){
        err := recover()
        if err != nil {
            fmt.Println("func B")
        }
    }()
    panic("func B error")
}
复制代码

注意

  1. recover必须搭配defer使用
  2. defer一定要再可能发panic的语句之前定义

练习题

  1. 分金币
/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
var (
  coins = 50
  users = []string{
    "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
  }
  distribution = make(map[string]int, len(users))
)
func main() {
  left := dispatchCoin()
  fmt.Println("剩下:", left)
}
// 我的解法
func dispatchCoin() int {
   coinSum := coins
   for _,person := range users{
      distribution[person] = 0
      nameLower := strings.ToLower(person)
      nameSlice := strings.Split(nameLower, "")
      for _,nameWord := range nameSlice {
         switch nameWord {
            case "e":
               distribution[person] += 1
               coinSum-=1
            case "i":
               distribution[person] += 2
               coinSum-=2
            case "o":
               distribution[person] += 3
               coinSum-=3
            case "u":
               distribution[person] += 4
               coinSum-=4
            default:
               break
         }
      }
   }
   for key,value := range distribution{
      fmt.Printf("%v分到了:%v个\n",key, value)
   }
   return coinSum
}
目录
相关文章
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
85 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
2月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
117 67
|
8天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
22 5
|
18天前
|
算法 安全 Go
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
38 14
|
18天前
|
Go 数据库
Go语言中的包(package)是如何组织的?
在Go语言中,包是代码组织和管理的基本单元,用于集合相关函数、类型和变量,便于复用和维护。包通过目录结构、文件命名、初始化函数(`init`)及导出规则来管理命名空间和依赖关系。合理的包组织能提高代码的可读性、可维护性和可复用性,减少耦合度。例如,`stringutils`包提供字符串处理函数,主程序导入使用这些函数,使代码结构清晰易懂。
63 11
|
18天前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
|
23天前
|
监控 安全 算法
深度剖析核心科技:Go 语言赋能局域网管理监控软件进阶之旅
在局域网管理监控中,跳表作为一种高效的数据结构,能显著提升流量索引和查询效率。基于Go语言的跳表实现,通过随机化索引层生成、插入和搜索功能,在高并发场景下展现卓越性能。跳表将查询时间复杂度优化至O(log n),助力实时监控异常流量,保障网络安全与稳定。示例代码展示了其在实际应用中的精妙之处。
39 9
|
2月前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
60 12
|
2月前
|
监控 算法 安全
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
33 1
|
2月前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数