#《Go 语音圣经(中文版)》

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package intset

import (
"bytes"
"fmt"
)

/*
@Time : 2021/6/8 15:48
@Author : onns
@File : ch6/intset/intset.go
*/

const platform = 32 << (^uint(0) >> 63)

type IntSet struct {
words []uint64
}

func (s *IntSet) Has(x int) bool {
word, bit := x/64, uint(x%64)
return len(s.words) > word && s.words[word]&(1<<bit) != 0
}

func (s *IntSet) Add(x int) {
word, bit := x/64, uint(x%64)
for word >= len(s.words) {
s.words = append(s.words, 0)
}
s.words[word] |= 1 << bit
}

func (s *IntSet) UnionWith(t *IntSet) {
for i, tword := range t.words {
if i < len(s.words) {
s.words[i] |= tword
} else {
s.words = append(s.words, tword)
}
}
}

func (s *IntSet) String() string {
var buf bytes.Buffer
buf.WriteByte('{')
for i, word := range s.words {
if word == 0 {
continue
}
for j := 0; j < 64; j++ {
if word&(1<<uint(j)) != 0 { // 这里为什么要用 uint() ?
if buf.Len() > len("{") {
buf.WriteByte(' ')
}
fmt.Fprintf(&buf, "%d", 64*i+j)
}
}
}
buf.WriteByte('}')
return buf.String()
}

func (s *IntSet) Len() int {
eleNum := 0
for _, word := range s.words {
if word == 0 {
continue
}
for j := 0; j < 64; j++ {
if word&(1<<j) != 0 {
eleNum += 1
}
}
}
return eleNum
} // return the number of elements

func (s *IntSet) AddAll(xList ...int) {
for _, x := range xList {
s.Add(x)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package intset

import (
"fmt"
"testing"
)

/*
@Time : 2021/6/10 15:00
@Author : onns
@File : ch6/intset/intset_test.go
*/

func Test(t *testing.T) {
var x, y IntSet
x.Add(1)
x.Add(144)
x.Add(9)
x.AddAll(5, 2, 3)
fmt.Println(x.String())
fmt.Println(x.Len())

y.Add(9)
y.Add(42)
fmt.Println(y.String())
fmt.Println(y.Len())

x.UnionWith(&y)
fmt.Println(x.String())
fmt.Println(x.Has(9), x.Has(123))

fmt.Println(&x)
fmt.Println(x.String())
fmt.Println(x)

fmt.Println(32 << (^uint(0) >> 63))
}
1
2
3
4
5
6
7
8
9
10
{1 2 3 5 9 144}
6
{9 42}
2
{1 2 3 5 9 42 144}
true false
{1 2 3 5 9 42 144}
{1 2 3 5 9 42 144}
{[4398046511662 0 65536]}
64

有几个疑问:

  1. 32位机器下可以定义int64吗?自行实现还是Go帮忙实现呢?

  2. 练习 6.5: 我们这章定义的IntSet里的每个字都是用的uint64类型,但是64位的数值可能在32位的平台上不高效。修改程序,使其使用uint类型,这种类型对于32位平台来说更合适。当然了,这里我们可以不用简单粗暴地除64,可以定义一个常量来决定是用32还是64,这里你可能会用到平台的自动判断的一个智能表达式:32 << (^uint(0) >> 63)
    那定义结构体的时候怎么比较优雅呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const platform = 32 << (^uint(0) >> 63)

    if platform == 32 {
    type IntSet struct {
    words []uint32
    }
    } else {
    type IntSet struct {
    words []uint64
    }
    }

    类似这种应该不合适吧?

  3. 其实方法到底是和对象本身还是它的地址绑定我还是有疑问,这种情况下可能会写出来bug我觉得。。。

#封装

Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。这种限制包内成员的方式同样适用于struct或者一个类型的方法。因而如果想要封装一个对象,必须将其定义为一个struct

这种基于名字的手段使得在语言中最小的封装单元是package,而不是像其它语言一样的类型。

一个struct类型的字段对同一个包的所有代码都有可见性,无论你的代码是写在一个函数还是一个方法里。

在命名一个getter方法时,通常会省略掉前面的Get前缀。

1
2
3
4
5
6
7
8
9
10
package log
type Logger struct {
flags int
prefix string
// ...
}
func (l *Logger) Flags() int
func (l *Logger) SetFlags(flag int)
func (l *Logger) Prefix() string
func (l *Logger) SetPrefix(prefix string)

Go的编码风格不禁止直接导出字段。[1]


  1. 导出怎么理解? ↩︎