接下来,go的条件与循环~
调用方法
定义方法:与给定类型的值相关联的函数。
示例:
package main
import (
"fmt"
"time"
)
func main() {
var now time.Time = time.Now()
var year int = now.Year()
fmt.Println(year)
}
time 包有一个表示日期(年、月、日)和时间(小时、分钟、秒等)的Time类型。每一个time.Time
值都有一个返回年份的Year方法。
time.Now 函数返回当前日期和时间的新Time值,将其存储在now变量中,然后对now引用的值调用Year方法。
方法是特定类型的值关联的函数
strings 包有一个Replacer类型,可以在字符串中搜索子字符串,并且在每次该子字符串出现的地方用另一个字符串替换它:
package main
import (
"fmt"
"strings"
)
func main() {
broken := "G# r#cks!"
replacer := strings.NewReplacer("#", "o")
fixed := replacer.Replace(broken)
fmt.Println(fixed)
}
strings.NewReplacer 函数接受要替换的字符串("#")和要替换为的字符串(“o”) 的参数,并返回给 strings.Replacer。当我们将一个字符串传递给 Replacer 值的 Replace 方法时,将返回一个完成了替换的字符串。
点表示右边的东西属于左边
函数属于一个包,方法属于一个单独的值,这个值出现在点的左边。
now.Year()
replacer.Replace(broken)
上边 now
为 值,Year
为 方法名
replacer
为 值, Replace
为方法名
评分
写一个程序:输入百分比分数,60% 分数及格,不足则不及格,输入的百分比大于或等于60,程序需要给出响应。
获取分数
输入百分比分数,按回车键,把输入的数字存在一个变量中。
当有一个值时,通常会分配一个变量,但不打算使用时,可以使用空白标识符。为空白标识符分配一个值实际上会丢弃它。在赋值语句中输入一个下划线 _
,通常在这里输入的是变量名。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
fmt.Println(input)
}
处理错误
若从 ReadString 方法中得到一个错误返回,空白标识符只会导致错误忽略,程序依然继续运行,可能会使用无效的数据。
log 包有一个 Fatal 函数,可以同时为我们完成两步:将一条消息记录到终端并停止程序运行。(在上下文中,Fatal 是报告一个错误,并杀死你的程序。)
去掉空白标识符用一个 err 变量替换,可以记录错误,然后将使用 Fatal 函数来记录错误并停止程序运行。
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
log.Fatal(err)
fmt.Println(input)
}
条件
若从键盘读取输入时遇到问题,将其设置为报告错误并停止运行,但是一切正常也停止运行。
ReadString 这样的函数和方法返回一个错误值nil,基本上意味着什么都没有,如果 err 为 nil ,表示没有错误,我们的程序被设置为只简单报告nil错误,正确做法是,当err变量的值不是nil时才退出程序。
可以使用条件语句,只有在满足某个条件时,才导致代码块(一个或多个由花括号{}
包围的语句)被执行的语句。
计算表达式,若结果为 true,则执行条件块体中的代码,若为false ,则跳过条件块。
if true {
fmt.Println("xxxx")
}
if 1 < 2 {
fmt.Println("xxxx")
}
go支持条件语句中的多个分支:
if grade == 100 {
fmt.Println("Perfect!")
} else if grade >= 60 {
fmt.Println("Pass!")
} else {
fmt.Println("Fail!")
}
条件语句依赖布尔表达式(计算结果为true或false)来决定是否执行包含的代码。
当在条件为假时执行,可以使用 !
布尔求反运算符。
if !true {
fmt.Println("xxx")
}
希望两个条件都为真时运行,使用&&
(“与”)运算符,两者之一为真时运行,使用||
(“或”)运算符。
if true && true {
fmt.Println("xxxxx")
}
if false || true {
fmt.Println("xxxxx")
}
go语言不要求 if 语句的条件用圆括号括起来。
有条件地记录致命错误
若 err 变量中的值为 nil ,表示从键盘读取成功。更新代码记录错误,在err不是 nil 时退出。
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
fmt.Println(input)
}
重新运行程序,可以看到可以正常运行,若读取用户输入有错误,也可以看到这些错误。
避免遮盖名字
避免使用缩写,但是命名 error变量不对,会遮盖一个名为 error 的类型名称。
声明变量时,应确保与任何现有的函数、包、类型或其他变量的名称不同。 若在封闭范围内存在同名的东西,变量将对其进行遮盖,也就是说优先级高,这不是一件好事。
但是若想访问变量遮盖的类型、函数或包,将得到变量中的值,此时,会导致编译错误。
将字符串转换为数字
strings
包中有个 TrimSpace
函数,删除字符串开头和结尾的所有空白字符(换行符、制表符和常规空格)。
s := "\t xxx \n"
fmt.Println(strings.TrimSpace(s))
字符串转换为数字,使用 strconv
包中的 ParseFloat
函数转换为 float64值。
grade, err := strconv.ParseFloat(input, 64)
块和变量的作用域
声明的每个变量都有一个作用域,可以在作用域内的任何地方被访问,但是在作用域之外访问会报错。
变量的作用域由其声明所在的块和嵌套在该块中任何块组成。
- packageVar 变量的作用域是整个 main 包,可以在包中定义的任何函数内的任何位置访问 packageVar。
- functionVar 变量作用域是其声明所在的整个函数,包括嵌套在该函数中的if块。
- conditionalVar 变量作用域仅限于if块,当试图在if块外访问conditionalVar,将报错。
修改后的程序
短变量声明中只有一个变量必须是新的
不能对一个变量声明两次。
但是当一个变量名在同一个作用域中被声明两次时,会编译报错。
但是,短变量声明中至少有一个变量名是新的,新变量名被视为声明,现有的被视为赋值。
练习
需求:
- 生成一个1到100之间的随机数,将其存储为目标数,供玩家猜测。
- 提示玩家猜测目标数是什么,存储他们的回答。
- 若玩家猜测的数小于目标数,就说“你猜低了”,若大于目标数,就说“你猜高了” 。
- 允许最多猜10次,在每次猜之前,让他们知道还剩多少次。
- 若猜的与目标数相同,就说“你猜对了”,然后不再问新的猜测。
- 若玩家用完了所有轮次也没猜对,就说“sorry,你没猜对,它是:[目标数]”
包名与导入路径
math/rand 包有一个 Intn 函数,可以生成一个随机数。
package main
import (
"fmt"
"math/rand"
)
func main() {
target := rand.Intn(100) + 1
fmt.Println(target)
}
math/rand
指的是包的导入路径,不是名称,导入路径是一个独特的字符串,用于标识包以及在导入语句中使用的包,一旦导入包,就可以通过包名来引用。
导入路径 | 包名 |
---|---|
“fmt” | fmt |
“log” | log |
“strings” | strings |
“archive” | archive |
“archive/tar” | tar |
“archive/zip” | zip |
“math” | math |
“math/cmplx” | cmplx |
“math/rand” | rand |
有些包,导入路径与包名都相同,有些包属于类似的类别,被分组在类似的导入路径前缀。
go语言不要求包名与其导入路径有任何关系,按惯例,导入路径的最后(或唯一)一段也用作包名,比如“archive” 包名为 archive,若导入路径为 “archive/zip” 则包名为 zip 。
生成随机数
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
seconds := time.Now().Unix()
rand.Seed(seconds)
target := rand.Intn(100) + 1
fmt.Println("I've chosen a random number between 1 and 100.")
fmt.Println("Can you guess it?")
fmt.Println(target)
}
从键盘获取整数
不需要将输入转换为float64,需要将其转换为int(因为只使用整数)。将把从键盘读取的字符串传递给 strconv 包的 Atoi (字符串转整数)函数,不是它的 ParseFloat 函数,Atoi会给一个整数作为其返回值。
将猜测与目标进行比较
下一步将用户的猜测与随机生成的数字进行比较;
若值低于目标值,打印信息,说猜低了,否则若猜测值大于目标值,打印信息说猜高了。
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("I've chosen a random number between 1 and 100.")
fmt.Println("Can you guess it?")
fmt.Println(target)
reader := bufio.NewReader(os.Stdin)
fmt.Print("Make a guess: ")
input, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
input = strings.TrimSpace(input)
guess, err := strconv.Atoi(input)
if err != nil {
log.Fatal(err)
}
if guess < target {
fmt.Println("your guess was low")
} else if guess > target {
fmt.Println("your guess was high")
}
}
循环
循环以 for 关键字开头:
- 一个初始化(或init)语句,用于初始化一个变量
- 一个条件表达式,用于决定何时中断循环
- 一个标志(post)语句,在循环的每次迭代后运行
++
与 --
经常用于循环的标志(post)语句中,++
在每次求值时都会加1, --
则会减1。
还包括赋值运算:
+=
加上另一个值,然后将结果赋回给该变量-=
减去另一个值,然后将结果赋回给该变量+=
与-=
可以在循环中用于1以外的增量计数。
for x := 1; x <= 5; x += 2 {
fmt.Println(x)
}
初始化和标志(post)语句是可选的
x := 1
for x <= 3 {
fmt.Println(x)
x++
}
使用“continue”与“break”跳过循环
go 提供了两个控制循环流的两个关键字:
- continue :立即跳转到循环的下一个迭代,不需要在循环块中运行任何其他代码
- break: 立即跳出循环,不再执行循环块中的代码,也不再运行循环,执行将移动到循环之后的语句中。
for x := 1, x <= 3; x++ {
fmt.Println("before continue")
continue
fmt.Println("after continue")
}
字符串“after continue”将永远不会打印输出。
for x := 1; x <= 3; x++ {
fmt.Println("before break")
break
fmt.Println("after break")
}
fmt.Println("after loop")
在循环第一次迭代中,“before break”被打印出,但随后break立即跳出循环,不打印 “after break”,也不再运行循环。
完整代码
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 //生成一个介于1-100之间的整数
fmt.Println("I've chosen a random number between 1 and 100.")
fmt.Println("Can you guess it?")
reader := bufio.NewReader(os.Stdin) //创建一个buffio.Reader 允许读取键盘输入
success := false //设置为默认打印失败信息
for guesses := 0; guesses < 10; guesses++ {
fmt.Println("you have", 10-guesses, "guesses left.")
fmt.Print("Make a guess: ")
input, err := reader.ReadString('\n') //读取用户输入,直到按enter键
if err != nil {
log.Fatal(err)
} //若出现错误则打印信息并退出
input = strings.TrimSpace(input) //删除换行符
guess, err := strconv.Atoi(input) //将输入字符转换为整数
if err != nil {
log.Fatal(err)
} //若出现错误则打印信息并退出
if guess < target {
fmt.Println("your guess was low")
} else if guess > target {
fmt.Println("your guess was high")
} else {
success = true //阻止显示失败信息
fmt.Println("good job! you guessed it!")
break //退出循环
}
}
if !success { //如果 “success” 是false,告诉玩家结果
fmt.Println("sorry,you didn't guess number.It was:", target)
}
}
在循环之后,添加了一个if块来打印失败消息,if块只有在条件的计算结果为true时运行,我们希望success为false时执行打印,所以添加布尔求反运算符!
。