Parcourir la source

feat: 新增第二课变量基础与第三课条件判断例程

zhuts il y a 4 jours
Parent
commit
1b3c2942d4

+ 13 - 5
README.md

@@ -11,10 +11,16 @@
 ├── go.mod                  # 所有例程共用的模块配置
 ├── .gitignore
 └── lessons/
-    └── 01_hello/           # 第 01 课:第一个 Go 程序
-        ├── README.md       # 本课目标、运行方法与练习
-        ├── main.go         # 本课独立入口
-        └── main_test.go    # 本课测试
+    ├── 01_hello/           # 第 01 课:第一个 Go 程序
+    │   ├── README.md       # 本课目标、运行方法与练习
+    │   ├── main.go         # 本课独立入口
+    │   └── main_test.go    # 本课测试
+    ├── 02_variables/       # 第 02 课:变量、常量与基础类型
+    │   ├── README.md       # 知识点、预期输出与手敲练习
+    │   └── main.go         # 带逐节注释的参考代码
+    └── 03_conditions/      # 第 03 课:if 条件判断
+        ├── README.md       # 语法、编译运行与边界练习
+        └── main.go         # 分支、比较和逻辑运算
 ```
 
 根目录不放例程代码。各课共享根目录的 `go.mod`,不需要逐课初始化模块,也不需要 `go.work`。当前模块路径 `example.com/go-study` 是学习用标识,不必与 Git 地址相同。
@@ -24,8 +30,10 @@
 | 级别 | 例程 | 内容 | 状态 |
 | --- | --- | --- | --- |
 | 01 | [第一个 Go 程序](./lessons/01_hello/README.md) | 输出、变量、函数、首次测试 | 已建立 |
+| 02 | [变量、常量与基础类型](./lessons/02_variables/README.md) | 声明、赋值、零值、类型转换 | 已建立 |
+| 03 | [if 条件判断](./lessons/03_conditions/README.md) | 条件分支、比较、逻辑运算 | 已建立 |
 
-后续按 `02_variables`、`03_conditions`、`04_loops` 等编号逐步新增独立目录,这些是命名示例,目前尚未创建。不一次性铺开后续课程,也不把新例程写进旧例程的入口文件。
+后续按 `04_loops` 等编号逐步新增独立目录,这些后续目录目前尚未创建。不一次性铺开后续课程,也不把新例程写进旧例程的入口文件。
 
 ## 快速开始
 

+ 99 - 0
lessons/02_variables/README.md

@@ -0,0 +1,99 @@
+# 第 02 课:变量、常量与基础类型
+
+## 怎么学习
+
+打开 [main.go](./main.go),按注释中的 1~7 节顺序照着敲一遍。每敲完一节,可以先运行,再继续下一节。不需要安装第三方依赖,也不需要重新初始化模块。
+
+如果想保留参考代码并另外手敲:在本课下创建 `practice/` 目录,在里面新建自己的 `main.go`。从仓库根目录用 `go run ./lessons/02_variables/practice` 运行;不要在本课同一目录里再创建第二个 `main` 函数。
+
+## 本课知识点
+
+| 写法 | 含义 | 注意 |
+| --- | --- | --- |
+| `var age int = 18` | 指定类型和初始值 | 类型写在变量名后面 |
+| `var city = "上海"` | 由初始值推断类型 | 这里推断为 `string` |
+| `lesson := 2` | 短变量声明 | 仅限函数内部 |
+| `age = 19` | 给已有变量赋新值 | 不是重新声明 |
+| `age, score := 20, 95` | 声明新变量并给已有变量赋值 | 同一作用域内至少有一个新变量 |
+| `const courseName = "Go 语言基础"` | 声明常量 | 不能重新赋值 |
+| `var count int` | 使用零值初始化 | 整数零值是 `0` |
+| `float64(quantity)` | 显式类型转换 | 不会改变原变量类型 |
+
+先掌握四种类型:`int`(整数)、`float64`(浮点数)、`string`(字符串)、`bool`(布尔值)。
+
+Go 不会自动把 `int` 变量转为 `float64` 来参与混合运算。浮点数转整数会向零截断,例如 `int(7.5)` 的结果是 `7`,不是四舍五入。
+
+## 运行
+
+在仓库根目录执行:
+
+```powershell
+go run ./lessons/02_variables
+```
+
+或从根目录进入本课:
+
+```powershell
+Set-Location lessons/02_variables
+go run .
+```
+
+预期输出:
+
+```text
+=== 个人资料 ===
+姓名:小朱,年龄:18,正在学习 Go:true
+城市:上海,类型:string
+当前是第 2 课,修改后的年龄:19
+再次修改后的年龄:20,分数:95
+课程:Go 语言基础
+
+=== 零值 ===
+int:0,float64:0.0,string:"",bool:false
+
+=== 购物计算 ===
+数量:3,单价:2.50,总价:7.50
+数量类型:int,单价类型:float64,总价类型:float64
+总价转为整数:7,原总价仍为:7.50
+```
+
+## 编译
+
+以下命令在仓库根目录执行:
+
+```powershell
+New-Item -ItemType Directory -Force bin/02_variables | Out-Null
+go build -o ./bin/02_variables/variables.exe ./lessons/02_variables
+.\bin\02_variables\variables.exe
+```
+
+`go build` 保留可执行文件;`go run` 先编译再直接运行。修改源码后,可执行文件需要重新编译才会更新。
+
+## 输出占位符
+
+| 占位符 | 用途 |
+| --- | --- |
+| `%s` | 字符串 |
+| `%d` | 十进制整数 |
+| `%t` | 布尔值 |
+| `%.2f` | 浮点数,输出保留两位小数 |
+| `%q` | 带引号的字符串,方便观察空字符串 |
+| `%T` | 值的类型 |
+| `\n` | 换行 |
+
+注意:`%.2f` 只是控制输出格式,不会把变量变成“精确的两位小数”。`float64` 可能存在精度误差,本例仅用于学习,实际金额处理需要另行设计。
+
+## 动手练习
+
+- [ ] 把姓名、城市和年龄改成自己的资料。
+- [ ] 将 `learning` 改为 `false`,观察输出。
+- [ ] 将商品数量改成 `4`、单价改成 `3.25`,核对总价 `13.00`。
+- [ ] 改为只计算 `quantity * unitPrice`,观察编译器的类型错误,再恢复转换。
+- [ ] 尝试给 `courseName` 重新赋值,观察常量不能修改的报错,再恢复。
+- [ ] 声明一个没有使用的局部变量,观察编译错误,再删除或使用它。
+
+故意制造错误的练习一次只做一个,观察后恢复可编译状态。不要同时保留多处错误。
+
+本课先专注变量与类型,不额外引入测试框架或函数封装。下一课再学习 `if` 条件判断。
+
+[上一课:第一个 Go 程序](../01_hello/README.md) · [学习总览](../../README.md)

+ 55 - 0
lessons/02_variables/main.go

@@ -0,0 +1,55 @@
+package main
+
+import "fmt"
+
+func main() {
+	// 1. 显式声明:var 变量名 类型 = 初始值。
+	var name string = "小朱"
+	var age int = 18
+	var learning bool = true
+
+	fmt.Println("=== 个人资料 ===")
+	fmt.Printf("姓名:%s,年龄:%d,正在学习 Go:%t\n", name, age, learning)
+
+	// 2. 类型推断:省略类型,由初始值推断。这里 city 的类型是 string。
+	var city = "上海"
+	fmt.Printf("城市:%s,类型:%T\n", city, city)
+
+	// 3. 短变量声明 := 只能在函数内部使用。
+	// 已经声明的变量,修改值时使用 =,而不是再次使用 :=。
+	lesson := 2
+	age = 19
+	fmt.Printf("当前是第 %d 课,修改后的年龄:%d\n", lesson, age)
+
+	// 同一作用域内,:= 左侧至少需要一个新变量。
+	// age 已存在,但 score 是新变量,所以这一行可以编译。
+	age, score := 20, 95
+	fmt.Printf("再次修改后的年龄:%d,分数:%d\n", age, score)
+
+	// 4. 常量:声明后不能重新赋值。
+	const courseName = "Go 语言基础"
+	fmt.Printf("课程:%s\n", courseName)
+
+	// 5. 零值:变量没有显式初始化时,会自动获得对应类型的零值。
+	var count int
+	var price float64
+	var message string
+	var finished bool
+
+	fmt.Println("\n=== 零值 ===")
+	// %q 为字符串加上引号,方便看出空字符串。
+	fmt.Printf("int:%d,float64:%.1f,string:%q,bool:%t\n", count, price, message, finished)
+
+	// 6. 类型转换:int 和 float64 不能直接相乘,需要显式转换。
+	quantity := 3
+	unitPrice := 2.5
+	total := float64(quantity) * unitPrice
+
+	fmt.Println("\n=== 购物计算 ===")
+	fmt.Printf("数量:%d,单价:%.2f,总价:%.2f\n", quantity, unitPrice, total)
+	fmt.Printf("数量类型:%T,单价类型:%T,总价类型:%T\n", quantity, unitPrice, total)
+
+	// 7. 浮点数转整数会向零截断,不会四舍五入,也不会修改原变量。
+	whole := int(total)
+	fmt.Printf("总价转为整数:%d,原总价仍为:%.2f\n", whole, total)
+}

+ 108 - 0
lessons/03_conditions/README.md

@@ -0,0 +1,108 @@
+# 第 03 课:if 条件判断
+
+## 学习目标
+
+第二课用变量保存数据,本课根据数据决定执行哪段代码。按 [main.go](./main.go) 中的 1~6 节逐段手敲、运行,暂不引入循环和键盘输入。
+
+本课独占一个目录,共享根目录的 `go.mod`,不需要安装第三方依赖或重新初始化模块。
+
+## 核心语法
+
+```go
+if 条件 {
+    // 条件成立
+} else if 另一个条件 {
+    // 前面的条件不成立,且当前条件成立
+} else {
+    // 前面的条件都不成立
+}
+```
+
+- 条件必须是 `bool` 类型,不像 C 那样可以用非零整数表示真。
+- 条件通常不加圆括号,大括号不能省略,左大括号与条件写在同一行。
+- `else` 与前面的 `}` 写在同一行。
+- 多分支从上到下判断,只执行第一个满足条件的分支,其余分支跳过。
+- 多个独立的 `if` 则会分别判断,不等于一个 `if / else if` 链。
+
+### 比较与逻辑运算
+
+| 运算符 | 含义 | 示例 |
+| --- | --- | --- |
+| `==` | 相等 | `language == "Go"` |
+| `!=` | 不相等 | `score != 100` |
+| `>`、`>=` | 大于、大于等于 | `age >= 18` |
+| `<`、`<=` | 小于、小于等于 | `score <= 100` |
+| `&&` | 并且,两侧都为真 | `age >= 18 && hasTicket` |
+| `\|\|` | 或者,至少一侧为真 | `score < 0 \|\| score > 100` |
+| `!` | 取反 | `!isClosed` |
+
+`=` 是赋值,`==` 才是比较相等。`&&` 和 `||` 会短路求值:左侧已经能决定结果时,不再计算右侧。
+
+## 编译并运行
+
+以下命令在**仓库根目录**执行:
+
+```powershell
+# 创建第三课的输出目录
+New-Item -ItemType Directory -Force bin/03_conditions | Out-Null
+
+# 编译第三课
+go build -o ./bin/03_conditions/conditions.exe ./lessons/03_conditions
+
+# 运行生成的程序
+.\bin\03_conditions\conditions.exe
+```
+
+修改源码后,要再次编译,旧的 exe 不会自动更新。
+
+快速编译并运行也可以使用:
+
+```powershell
+go run ./lessons/03_conditions
+```
+
+或者从仓库根目录进入本课后执行:
+
+```powershell
+Set-Location lessons/03_conditions
+go run .
+```
+
+`go run` 仍会先编译再运行,不是解释执行。
+
+## 预期输出
+
+```text
+=== 单条件判断 ===
+年龄:20,已成年
+
+=== 是否及格 ===
+分数:75,及格
+
+=== 成绩等级 ===
+等级:及格
+
+=== 逻辑运算 ===
+已成年、有票且未闭馆,可以入场
+周末或节假日,可以安排休息
+
+=== 相等与不相等 ===
+当前学习的语言是 Go
+本次成绩不是满分
+
+=== 显式布尔条件 ===
+数量不为零
+```
+
+## 动手练习
+
+- [ ] 把 `age` 改为 `17`,观察成年判断和入场判断的变化。
+- [ ] 将 `score` 依次改为 `59`、`60`、`79`、`80`、`89`、`90`、`100`,检查分数边界。
+- [ ] 将 `score` 改为 `-1` 或 `101`,观察成绩等级的非法分数提示。前面的“是否及格”只是二分支语法示范,尚未验证范围;试着给它也加入范围检查。
+- [ ] 把 `hasTicket` 改为 `false`,或把 `isClosed` 改为 `true`,观察入场结果。
+- [ ] 把 `isWeekend` 和 `isHoliday` 都改为 `false`,观察休息提示是否还会输出。
+- [ ] 把 `count` 改为 `0`,观察最后一条提示是否消失。
+
+先手动验证这些输入,不急着封装函数或引入更复杂测试。下一课再学习 `for` 循环。
+
+[上一课:变量与基础类型](../02_variables/README.md) · [学习总览](../../README.md)

+ 73 - 0
lessons/03_conditions/main.go

@@ -0,0 +1,73 @@
+package main
+
+import "fmt"
+
+func main() {
+	// 1. 单条件判断:条件为 true 时,执行大括号中的代码。
+	// Go 的条件不需要圆括号,但大括号不能省略。
+	fmt.Println("=== 单条件判断 ===")
+	age := 20
+	if age >= 18 {
+		fmt.Printf("年龄:%d,已成年\n", age)
+	}
+
+	// 2. 二选一:条件成立执行 if,否则执行 else。
+	// else 必须和前面的右大括号 } 写在同一行。
+	fmt.Println("\n=== 是否及格 ===")
+	score := 75
+	if score >= 60 {
+		fmt.Printf("分数:%d,及格\n", score)
+	} else {
+		fmt.Printf("分数:%d,不及格\n", score)
+	}
+
+	// 3. 多分支:从上往下判断,只执行第一个满足条件的分支。
+	// 先排除非法分数,再按分数从高到低判断,避免优秀被及格分支截走。
+	fmt.Println("\n=== 成绩等级 ===")
+	if score < 0 || score > 100 {
+		fmt.Println("分数无效,应在 0~100 之间")
+	} else if score >= 90 {
+		fmt.Println("等级:优秀")
+	} else if score >= 80 {
+		fmt.Println("等级:良好")
+	} else if score >= 60 {
+		fmt.Println("等级:及格")
+	} else {
+		fmt.Println("等级:不及格")
+	}
+
+	// 4. 逻辑运算:&& 是并且,|| 是或者,! 是取反。
+	fmt.Println("\n=== 逻辑运算 ===")
+	hasTicket := true
+	isClosed := false
+	if age >= 18 && hasTicket && !isClosed {
+		fmt.Println("已成年、有票且未闭馆,可以入场")
+	} else {
+		fmt.Println("暂时不能入场")
+	}
+
+	isWeekend := false
+	isHoliday := true
+	if isWeekend || isHoliday {
+		fmt.Println("周末或节假日,可以安排休息")
+	}
+
+	// 5. 比较运算:== 判断相等,!= 判断不相等。
+	// = 是赋值,不是判断相等。
+	fmt.Println("\n=== 相等与不相等 ===")
+	language := "Go"
+	if language == "Go" {
+		fmt.Println("当前学习的语言是 Go")
+	}
+	if score != 100 {
+		fmt.Println("本次成绩不是满分")
+	}
+
+	// 6. Go 的条件必须是 bool,不能像 C 一样用整数表示真假。
+	fmt.Println("\n=== 显式布尔条件 ===")
+	count := 1
+	// if count { } // 错误:int 不能直接作为条件。
+	if count != 0 {
+		fmt.Println("数量不为零")
+	}
+}