2.条件与循环

Wednesday, May 31, 2023

接下来,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时执行打印,所以添加布尔求反运算符!

Golang打怪升级

3.函数

1-1.golang25个保留关键字

comments powered by Disqus