Go 编程的三个常见问题

共 2401字,需浏览 5分钟

 ·

2020-09-21 14:20

via:

https://medium.com/higher-order-functions/golang-three-common-programming-problems-3ef8baf006af
作者:Saurabh Nayar

四哥水平有限,如有翻译或理解错误,烦请帮忙指出,感谢!

作者在文章中列举了使用 Go 语言过程碰到的三个常见问题,并且都给出了解决方法,一起来看下作者是怎么解决的!

原文如下:


每种语言都是独一无二的。这些常见编程问题的解决方案在 Java 中是非常不同的 -- Java 是我以前最喜欢的编程语言。我敢这么说,这些问题的解决方法如果使用 Java 来解决会更加直观。

Go 语言有解决这些问题的独特方法。我下面列出的解决方案最初对我来说不是很直观,但是现在已经成为我的下意识的反映。我不确定这些解决方法是否是地道的 Go 语言解决方式,老实说,我也不知道地道的方式是怎么样的。

也许会有更好的、不同的方式来解决这些问题 -- 我想听听你的想法。

现在,我们一起来研究下这些问题。

问题一

问题:我需要维护一个集合,但是 Go 语言里面没有集合这种数据结构。

解决办法之一:可以用 Go 语言里面的 map 代替集合,map 中的 key 都是唯一的。

package main

import "fmt"

type Set struct {
 m map[string]bool
}

func NewSet() Set {
 m := make(map[string]bool)
 return Set{m: m}
}

func (s *Set) Contains(val string) bool {
 _, ok := s.m[val]
 return ok
}

func (s *Set) Add(val string) {
 s.m[val] = true
}

func (s *Set) Remove(val string) {
     delete(s.m, val)
}

func main() {
   s := NewSet()
 s.Add("foo")
 fmt.Printf("s has foo: %t. s has bar: %t\n", s.Contains("foo"), s.Contains("bar"))

  s.Remove("foo")

 fmt.Printf("s has foo: %t. s has bar: %t\n", s.Contains("foo"), s.Contains("bar"))

}

使用 map 作为集合的底层数据结构的好处在于,map 基于 hash 表实现,减值查找效率高。使用这种方法可以少写很多代码。

问题二

问题:我想比较两个值大小,但是 == 操作符有时会失效。

解决办法之一:我们需要理解 == 操作符的适用场景。

包含 map 或者 slice 的结构体

type ABC struct {
   a int
   b string
   c []int
}
Error:
invalid operation: a == b (struct containing []int cannot be compared)

包含指针的结构体

从实际意义上讲,指针可以进行比较,但事实总是出乎意料。

a, b := 11
fmt.Println(&a == &b) // False

使用 reflect.DeepEqual

//ABC - A simple type
type ABC struct {
   a int
   b string
   c []int
}

var a = ABC{a: 1, b: "10", c: []int{12}}
var b = ABC{a: 1, b: "10", c: []int{12}}
reflect.DeepEqual(a, b)

Example #2
a, b := 11
fmt.Println(&a == &b) // False
fmt.Println(reflect.DeepEqual(&a, &b)) // True

reflect.DeepEqual 可以实现更好的效果。但是如果结构体中 float 或者 时间字段想要忽略,则需要自己编写比较函数。

//ABC - A simple type
type ABC struct {
   a int
   b string
   t time.Time // Ignore time while comparing to structs
}

var a = ABC{a: 1, b: "10", t: time.Now()}
var b = ABC{a: 1, b: "10", t: time.Now()}
fmt.Println(a == b, equals(a, b))

func equals(val1, val2 ABC) bool {
    return val1.a == val2.a && val1.b == val2.b
}

除非别无选择,否则一般都不会自己编写比较函数。但是与 == 操作符相比,是否要倾向于使用 reflect.DeepEqual。本质上,如果 == 比较的结果为 true,则 reflect.DeepEqual 可以保证比较的结果为 true,反之为 false。所以你可以默认使用 reflect.DeepEqual,除非程序上有性能上的限制:

func BenchmarkOperator(t *testing.B) {
   for i := 0; i < t.N; i++ {
      if a == b {
      }
   }
}

func BenchmarkReflectDeep(t *testing.B) {
   for i := 0; i < t.N; i++ {
      if reflect.DeepEqual(a, b) {
      }
   }
}
BenchmarkOperator-8         44614131            24.8 ns/op         0 B/op          0 allocs/op
BenchmarkReflectDeep-8        823174          1558 ns/op          96 B/op          2 allocs/op

从结果看出,== 比 reflect.DeepEqual 快多了!

问题三

问题:我需要使用一个 struct 作为 map 的键 --  但 struct 有想要忽略的 slice、指针或别的字段。

解决办法之一:Go 语言里面使用 == 操作符比较 map 的键,而不要使用  reflect.DeepEqual。

解决问题的方法之一就是自定义 key 的创建逻辑。

//Obvious solution that will not work
type A struct {
    i *int
}

i, j := 11
a, b := A{i: &i}, A{i: &j}
m := map[A]bool{}
m[a] = true
_, ok := m[b]
fmt.Println(ok) // False key b doesn't exist in map m


//Custom keys- solution
func customKey(a A) int {
 return *a.i
}

i, j := 11
a, b := A{i: &i}, A{i: &j}
m := map[int]bool{}
m[customKey(a)] = true
_, ok := m[customKey(b)]
fmt.Println(ok)// This will return true

有奖问答

问题:从上面问题 2 和问题 3 衍生出一个问题,如何比较两个 map?

key, val := "key""val"
key1, val1 := "key""val"
abc := map[*string]string{&key: val}
abc2 := map[*string]string{&key1: val1}
def := map[string]*string{key: &val}
def2 := map[string]*string{key1: &val1}
fmt.Println(reflect.DeepEqual(abc, abc2)) //false 
fmt.Println(reflect.DeepEqual(def, def2)) //true

首先需要注意的是,不能使用 == 操作符比较 map,需要使用  reflect.DeepEqual。根据 reflect.DeepEqual 的比较规则,map 的键使用 == 操作符比较而值会使用 reflect.DeepEqual  递归比较。




推荐阅读



学习交流 Go 语言,扫码回复「进群」即可


站长 polarisxu

自己的原创文章

不限于 Go 技术

职场和创业经验


Go语言中文网

每天为你

分享 Go 知识

Go爱好者值得关注


浏览 35
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报