提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
本文是《Head First Go》的第二章,条件和循环
1. 调用方法
下面通过调用 time 包里面的 Time 类型的方法来获取年份
package main
import(
"fmt"
"time"
)
func main() {
var now time.Time = time.Now() //time.Now返回一个代表当前日期和时间的time.Time值
var year int = now.Year() //获取年, 调用now.Year方法
fmt.Println(year) //2022
}
方法是与特定类型的值关联的函数
strings包有一个 Replacer 类型,可以在字符串中搜索子字符串,并且在每次该字符串出现的地方用另一个字符串替换它。
package main
import (
"fmt"
"strings"
)
func main() {
broken := "G# r#cks!"
replacer := strings.NewReplacer("#", "o"); //将字符串中的#字符替换成o字符
fixed := replacer.Replace(broken) //进行替换
fmt.Printf("fixed: %v\n", fixed) //fixed: Go rocks!
}
now.Year() 和 strings.NewReplacer(“#”, “o”) 都是变量在左边,通过 . 的方式来调用方法名,方法名大写开头,意思是可以导入的。
2. 注释
两种注释方法:
package main
import (
)
func main() {
//注释方法1:行注释
/*
注释方法2:块注释
*/
}
3. 获取用户的分数
1. 输入数字
引出一个需求就是用户可以输入数字:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Print("Enter a grade: a")
reader := bufio.NewReader(os.Stdin) //设置从键盘获取文本的"缓冲读取器"
input, _ := reader.ReadString('\n') //返回用户输入的所有内容。直到按下<Enter>键为止
fmt.Printf("input: %v\n", input)
}
首先,我们需要让用户知道需要输入某些东西,所以我们使用fmt.Print
函数来显示一个提示符。接下来从bufio.NewReader(os.Stdin)
中获取标准输入bufio.Reader
,调用 Reader 的 ReadString 方法。ReadString 方法需要一个带有 rune 字符的参数,意思是遇到这个字符就停止读取,所以我们给了 ‘\n’,只要遇到 ‘\n’ 就停止读取,只读取 ‘\n’ 之前的字符,最后打印出来。
但是如果我们改一下:input, _ := reader.ReadString('\n')
改成 input := reader.ReadString('\n')
, 就会报错,因为函数有两个返回值,而我们只是获取了其中一个
在Go语言中,一个函数可以返回任意数量的值,Go中多个返回值最常见的用法就是返回一个额外的错误值,可以通过查询该错误来确定函数或方法运行时是否发生了错误,举几个例子:
b, err := strconv.ParseBool("true") //如果字符串无法转换未布尔值
f, err2 := os.Open("myfile.txt") //如果文件无法打开,就返回一个错误
r, err3 := http.Get("heep://golang.org") //如果无法检索页面,就返回错误
回到上面的代码,如果我们只是使用一个变量接收函数返回值,就会报错
2. 解决方法
第一个解决方法是使用空白标识符忽略错误返回值,也就是上面用的那种。当我们有一个值,通常会分配给一个变量,但是我们不打算使用时,可以使用Co的空白标识符,只需要在赋值语句中输入一个下划线(_)字符,就可以忽略这个返回值了。但是有一个弊端就是我们无法解决错误,因为不管有没有错误,程序都会继续执行。
第二个解决方法是解决错误。 log 包下面有一个 Fatal 函数,它可以同时为我们完成这两项操作:将一条消息记录到终端并停止程序运行。(Fatal意味者报告一个错误,并 “杀死” 你的程序)
4. 条件
按照上面的例子,虽然我们输出了err,但是程序无论正确与否都会停止运行,所以我们需要使用条件来判断。首先 Go 的 if 语法if 1 < 2 { 逻辑 }
。和 java 的不同点就是 Go 可以省略括号,有了这个语法,我们可以把程序改造成下面的这样。
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin) //设置从键盘获取文本的"缓冲读取器"
input, err := reader.ReadString('\n') //返回用户输入的所有内容。直到按下<Enter>键为止
if(err != nil){
log.Fatal(err)
}
fmt.Printf("input: %v\n", input)
}
此外,简单介绍下 if 用法:与或非
package main
import "fmt"
func main() {
if 3 > 2 {
fmt.Println("1 == 1")
}
if 2 < 1{
fmt.Println("1 == 2")
}
if true && false {
fmt.Println("true && false")
}
if true || false {
fmt.Println("true || false")
}
if !false {
fmt.Println("!false")
}
/*
1 == 1
true || false
!false
*/
}
5. 避免遮盖名字
和 java 一样,尽量不要把变量写成和类型名称相同,同样也不要写成和函数名相同。下面的例子就可以看到了,如果我们提前声明了 int 名字的类型,那么下面再声明 int 类型变量的时候这个 int 就是变量名而不是 int 类型了。
package main
import "fmt"
func main() {
var int int = 12
var append string = "minutes of bonus footage"
var fmt string = "DVD"
var count int //报错int (variable of type int) is not a type
fmt.println(int, append. fmt, count);
}
6. 将字符串转换为数字
接着上面的例子,我们现在要输入一个数字,然后把这个字符串转化成数字类型的数据,最终比较。下面流程
- 首先使用 strings 包下面的 TrimSpace 函数,删除字符串开头和结尾的所有空白字符(‘\n’,‘\t’,’ ')
- 然后使用 strconv 包下的 ParseFloat 方法转化字符串
- 最终输出即可
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin) //设置从键盘获取文本的"缓冲读取器"
input, err := reader.ReadString('\n') //返回用户输入的所有内容。直到按下<Enter>键为止
if(err != nil){
log.Fatal(err)
}
input = strings.TrimSpace(input)
f, err2 := strconv.ParseFloat(input, 64)
if(err2 != nil){
log.Fatal(err2)
}
var status string
if f >= 60{
status = "大于60分"
}else{
status = "小于60分"
}
fmt.Printf(status)
//Enter a grade: 90
//大于60分
}
7. 块
Go代码可以分成块,也就是代码段。块通常由({ })包围,尽管在源代码文件和包级别也有块。块可以彼此嵌套。
注意的是在 if 块中声明的变量在 if 块外部无法使用。
8. 块和变量的作用域
变量的作用域由其声明所在的块和嵌套在块中的任何块组成
package main
import "fmt"
var packageVar = "package" //作用域整个main函数
func main() {
var functionVar = "funcation" //作用域是main函数
if true {
var condition = "conditional" //作用域是if块
fmt.Println(condition)
fmt.Println(functionVar)
fmt.Println(packageVar)
}
fmt.Println(functionVar)
fmt.Println(packageVar)
}
//conditional
//funcation
//package
//funcation
//package
package
作用域是整个main包functionVar
作用域是它声明的整个函数condition
作用域仅限于 if 块,在 if 块之外使用会报错(undeclared name: condition)
9. 短变量声明中只有一个变量必须是新的
来看这一段代码
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
s, err := reader.ReadString('\n')
f, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
fmt.Printf("f: %v\n", f)
fmt.Printf("err: %v\n", err)
}
可以看到里面 err 中短变量声明了两次,确实,当同一个变量名在同一个作用域中被声明了两次的时候,我们会得到一个编译错误
a := 1
a := 1
但是只要短变量声明中至少有一个变量名是新的,这是允许的。新变量名被视为声明,而现有的名字被视为赋值。如下例子:a 和 b 在下面的语句中,a 和 b 被重新赋值,这里不是声明的含义,Go 语言允许这样的语法。
package main
import (
"fmt"
)
func main() {
a := 1
a, b := 2, 3
b, c := 3, 4
fmt.Printf("a: %v\n", a) //a: 2
fmt.Printf("b: %v\n", b) //b: 3
fmt.Printf("c: %v\n", c) //c: 4
}
10. 创建一个游戏
- 生成一个 1 到 100 之间的随机数,并将其存储为目标数,供玩家猜测
- 提示玩家猜测目标数是什么,并存储他们的回答
- 如果玩家猜测小于目标树,输入 “低了” ,高于就输出 “高了”
- 允许玩家猜测10次,在每次猜之前,让他们知道还剩下多少次
- 如果猜测的数和目标树相同提示正确
- 如果玩家猜测次数用完了也没有猜出,提示次数使用完
11. 包名和导入路径
package main
import (
"fmt"
"math/rand"
)
func main() {
target := rand.Intn(100) + 1
fmt.Printf("target: %v\n", target) //target: 82
}
"math/rand"
导入 math. rand 包,fmt 导入 fmt 包,类似于磁盘上面的路径,下面举一个例子
导入路径 | 包名 |
---|---|
“archive” | archive |
“archive/tar” | tar |
“archive/zip” | zip |
“math” | math |
“math/cmplx” | cmplx |
“math/rand” | rand |
12. 生成随机数
导入之后,直接使用 rand 包直接调用函数生成随机数。rand.Intn
返回一个介于 0 和你提供的数字之间的随机整数。也就是说如果我们传入100,那么结果会获得一个 0 ~ 99 之间的随机数,我们需要 1 ~ 100 之间的,那么就在原来基础上 +1 就行了。
但是值得注意的是,我们在程序直接调用之后生成的一直都是一个数,那么我们要生成不同的随机数,就要通过 rand.Seed 函数传递一个种子,从而生成不同的随机数
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
seconds := time.Now().Unix() //获取当前日期和时间的整数形式
rand.Seed(seconds) //设置种子
target := rand.Intn(100) + 1
fmt.Printf("target: %v\n", target) //target: 22
}
13. for 循环
这个游戏里面有一个需求是玩家最多能猜测 10 次,对于这个请求,我们可以使用 for 循环
for x := 4; x <= 10; x++{
//代码块
}
同样的,x++(每次自增+1),x–(每次减少1),x += 等语法都可以使用,而且同样的 for 循环变量的作用域不能在循环外面使用。语法和 java 的一样,只是少了括号,Go语言不需要加上括号,同时也不允许加上括号。
package main
import (
"fmt"
// "math/rand"
// "time"
)
func main() {
var i = 0
for i = 0; i < 10; i++{
y := 1 //y只能在for循环中使用
fmt.Printf("y: %v\n", y) //只能在for循环之外访问
fmt.Printf("i: %v\n", i)
}
//i: 0
//i: 1
//i: 2
//i: 3
//i: 4
//i: 5
//i: 6
//i: 7
//i: 8
//i: 9
}
14. 使用 continue 和 break 跳过部分循环
continue:跳过本次循环,进入下一次循环
break:跳出本层循环
下面简单写个例子:
package main
import (
"fmt"
// "math/rand"
// "time"
)
func main() {
for i := 0; i < 10; i++ {
if i == 2{
continue //遇到2就跳过
}
fmt.Print(i, " ")
}
//结果:0 1 3 4 5 6 7 8 9
fmt.Println();
for i := 0; i < 10; i++ {
if i == 2{
break //遇到2就退出
}
fmt.Print(i, " ")
}
//结果:0 1
}
15. 最终的代码
最终来整体写一下第10题的代码
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
seconds := time.Now().Unix() //获取秒
rand.Seed(seconds) //设置时间种子
target := rand.Intn(100)+1; //设置目标数
fmt.Println("现在已经生成一个数,是在 1 ~ 100 之间,你能猜出来吗");
reader := bufio.NewReader(os.Stdin) //创建一个reader
success := false //是否成功了
for start := 0; start < 10; start++{
fmt.Println("现在你还有" , (10-start) , "次机会, 请输入一个数:");
input, err := reader.ReadString('\n') //读取用户输入的数字
if err != nil{
log.Fatal(err);
}
input = strings.TrimSpace(input)
//把数的数字转化为整数
res, err := strconv.Atoi(input)
if err != nil{
log.Fatal(err);
}
if res > target{
fmt.Println("你输入的数据比目标值要大");
}else if res < target {
fmt.Println("你输入的数据比目标值要小");
}else{
success = true; //设置为true
fmt.Println("恭喜你找到了!!!");
break; //退出循环
}
}
if !success {
fmt.Println("你没有找到目标值!!!");
}
/*
现在已经生成一个数,是在 1 ~ 100 之间,你能猜出来吗
现在你还有 10 次机会, 请输入一个数:
98
你输入的数据比目标值要大
现在你还有 9 次机会, 请输入一个数:
50
你输入的数据比目标值要大
现在你还有 8 次机会, 请输入一个数:
25
你输入的数据比目标值要大
现在你还有 7 次机会, 请输入一个数:
12
你输入的数据比目标值要小
现在你还有 6 次机会, 请输入一个数:
20
你输入的数据比目标值要小
现在你还有 5 次机会, 请输入一个数:
23
你输入的数据比目标值要大
现在你还有 4 次机会, 请输入一个数:
22
恭喜你找到了!!!
*/
}
如有错误,欢迎指出!!!