这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
直接上课堂中讲的项目
猜谜游戏
rand.Intn()为什么每次执行都是同一个数字
- 随机数种子
- 使用
rand.Seed(time.Now().UnixNano())函数可以设置种子,这样每次返回的随机数就不相同
直接上课堂中将的项目
- 猜数字
-
-
-
- 主要知识点
-
-
-
-
rand
-
-
-
-
rand.Intn(n int) int为什么每次执行都是同一个数字?- 从函数描述中看,Intn返回一个[0,n)的
int数字,如果n小于等于0就会发生panic - 使用相同的值播种会导致每次运行的随机序列相同,不设置
rand.Seed()则默认设置的为rand.Seed(1),可以从函数描述中看到
-
-
var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)})
// Seed 使用提供的种子值将默认 Source 初始化为
// 确定性状态。 如果没有调用 Seed,则生成器的行为如下
// 如果由 Seed(1) 播种。 具有相同余数的种子值
// 除以 2³¹-1 生成相同的伪随机序列。
// 与 Rand.Seed 方法不同,Seed 对并发使用是安全的。
func Seed(seed int64) { globalRand.Seed(seed) }
-
-
-
- 对于不同的数字,使用不同的值作为种子
- 例如 time.Now().UnixNano(),它会产生一个不断变化的数字。
- 所以在每次执行程序生成随机数之前调用
rand.Seed(time.Now().UnixNano())使得每次执行程序获得的函数都不一样
-
-
-
-
- 命令行输入
-
-
-
-
- 方法
-
-
-
-
-
-
bufio
-
-
-
-
-
-
-
-
- 创建一个reader对象,
reader := bufio.NewReader(os.Stdin) - 这里
os.Stdin是系统标准输入,就是从命令行中读取 - 然后读取一行的数据
input, err := reader.ReadString('\n'),这里的一行是用'\n'来控制的,也就是这个原因,不同系统中的换行符不一样,在linux系统中使用\n,在windows中应使用\r\n``reader.ReadString(b byte)是读到第一个b字节出现为止,且会将那一个字节读入字符串,返回读到的字符串 - 除了使用
ReadString(b byte)方法读取一行以外,还可使用func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)来实现,返回一个字节数组,不会包括换行符
- 创建一个reader对象,
-
-
-
-
- ReadLine 是一个低级的行读取原语。 大多数调用者应该改用 ReadBytes('\n') 或 ReadString('\n') 或使用 Scanner。
- ReadLine 尝试返回单行,不包括行尾字节。 如果该行对于缓冲区来说太长,则设置 isPrefix 并返回该行的开头。 该行的其余部分将从以后的调用中返回。 返回该行的最后一个片段时,isPrefix 将为 false。 返回的缓冲区仅在下一次调用 ReadLine 之前有效。 ReadLine 要么返回非零行,要么返回错误,从不返回两者。
- 从 ReadLine 返回的文本不包括行尾(“\r\n”或“\n”)。 如果输入结束时没有最后一行结束,则不会给出指示或错误。 在 ReadLine 之后调用 UnreadByte 将始终未读取读取的最后一个字节(可能是属于行尾的字符),即使该字节不是 ReadLine 返回的行的一部分。
-
-
-
-
fmt
-
-
-
-
-
-
-
-
fmt.Scan()直接传入指针,从标准输入中读取数据fmt.Scanf()传入format格式和相应需要赋值的变量的指针,根据格式去读取数据
-
-
-
-
// Scan 扫描从标准输入读取的文本,存储连续的
// 空格分隔的值到连续的参数中。 换行数
// 作为空间。 它返回成功扫描的项目数。
// 如果小于参数个数,err 会报告原因。
func Scan(a ...interface{}) (n int, err error)
// Scanf 扫描从标准输入读取的文本,存储连续的
// 以空格分隔的值到由下式确定的连续参数中
// 格式。 它返回成功扫描的项目数。
// 如果小于参数个数,err 会报告原因。
// 输入中的换行符必须与格式中的换行符匹配。
// 一个例外:动词 %c 总是扫描
// 输入,即使是空格(或制表符等)或换行符。
func Scanf(format string, a ...interface{}) (n int, err error) {
return Fscanf(os.Stdin, format, a...)
}
- 字典查询
-
- 两个工具
-
- 发送请求部分比较简单,主要关注两个翻译引擎并发查询
- golang中启动协程直接使用
go关键字即可 - 为了父协程等待子协程执行完后才能结束,使用
sync.WaitGroup来控制 sync.WaitGroup适合于知道几个子协程的情况下使用- 使用
Wait()方法来等待子协程执行完成 - 因为子协程中两个都需要打印到标准输出,在并行的情况下会出现乱序的情况,所以我们引入
channel来管理
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
`)
os.Exit(1)
}
word := os.Args[1]
var wg sync.WaitGroup
ch := make(chan struct{})
wg.Add(2)
go query(word, &wg, ch)
go queryByYouDao(word, &wg, ch)
wg.Wait()
}
-
- 使用
Add(n int)设置需要等待的子协程个数,Wait()进行阻塞等待 - 这里因为只需要控制打印输出的顺序,channel只需要传输一个信号即可,不需要传输具体的数据,所以这里定义的是一个传输空结构体的channel来提升效率
- 在子协程中则是以下逻辑
- 使用
func query(word string, wg *sync.WaitGroup, c chan struct{}) {
defer wg.Done()
// HTTP请求并打印翻译结果
// 传输一个信号表示打印完成,避免结果乱序
c <- struct{}{}
}
func queryByYouDao(word string, wg *sync.WaitGroup, c chan struct{}) {
defer wg.Done()
// http请求数据
// 阻塞等待,这里channel传输的数据无意义,只作为一个信号量使用
<-c
// 打印翻译结果
}