diff --git a/stage03-functions/03-closures/main.go b/stage03-functions/03-closures/main.go new file mode 100644 index 0000000..fe5fb0e --- /dev/null +++ b/stage03-functions/03-closures/main.go @@ -0,0 +1,116 @@ +package main + +import "fmt" + +func exercise1() func(int) int { + fmt.Println("=== 练习1:基础闭包 ===") + + // 创建一个闭包函数,它捕获外部变量multiplier + // 该闭包接收一个int参数并返回乘以multiplier的结果 + + multiplier := 3 + ____ := func(num int) ____ { + return ____ + } + + return ____ +} + +func exercise2() func() int { + fmt.Println("=== 练习2:闭包状态保持 ===") + + // 创建一个递增计数器闭包 + // 每次调用返回的函数时,计数器加1 + // 提示:使用匿名函数捕获外部变量 + + count := ____ + + ____ := func() ____ { + ____ + return ____ + } + + return ____ +} + +func exercise3() func() int { + fmt.Println("=== 练习3:斐波那契数列闭包 ===") + + // 创建一个生成斐波那契数列的闭包 + // 每次调用返回下一个斐波那契数 + // 数列: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34... + + a, b := ____, ____ + + ____ := func() ____ { + result := ____ + a, b = ____ + return ____ + } + + return ____ +} + +func exercise4() func(int) bool { + fmt.Println("=== 练习4:闭包配置 ===") + + // 创建一个检查数字是否在范围内的闭包生成器 + // 该生成器接受min和max两个参数,返回一个检查函数 + // 检查函数判断输入数字是否在[min, max]范围内 + + min := 10 + max := ____ + + ____ := func(num int) ____ { + return num >= ____ && num <= ____ + } + + return ____ +} + +func exercise5(start int) func(int) int { + fmt.Println("=== 练习5:闭包累加器 ===") + + // 创建一个累加器闭包 + // 从start开始,每次调用累加参数值 + // 提示:闭包应该能访问外部作用域的变量 + + total := ____ + + ____ := func(n int) ____ { + ____ + return ____ + } + + return ____ +} + +func main() { + fmt.Println("=== 闭包 练习 ===\n") + + add3 := exercise1() + fmt.Printf("练习1结果: add3(4) = %d\n", add3(4)) + fmt.Printf("练习1结果: add3(5) = %d\n", add3(5)) + + counter := exercise2() + fmt.Printf("练习2结果: counter() = %d\n", counter()) + fmt.Printf("练习2结果: counter() = %d\n", counter()) + fmt.Printf("练习2结果: counter() = %d\n", counter()) + + fib := exercise3() + fmt.Printf("练习3结果: fib() = %d\n", fib()) + fmt.Printf("练习3结果: fib() = %d\n", fib()) + fmt.Printf("练习3结果: fib() = %d\n", fib()) + fmt.Printf("练习3结果: fib() = %d\n", fib()) + fmt.Printf("练习3结果: fib() = %d\n", fib()) + + inRange := exercise4() + fmt.Printf("练习4结果: inRange(15) = %t\n", inRange(15)) + fmt.Printf("练习4结果: inRange(5) = %t\n", inRange(5)) + fmt.Printf("练习4结果: inRange(20) = %t\n", inRange(20)) + + accumulator := exercise5(100) + fmt.Printf("练习5结果: accumulator(10) = %d\n", accumulator(10)) + fmt.Printf("练习5结果: accumulator(20) = %d\n", accumulator(20)) + fmt.Printf("练习5结果: accumulator(30) = %d\n", accumulator(30)) +} diff --git a/stage03-functions/03-closures/main_test.go b/stage03-functions/03-closures/main_test.go new file mode 100644 index 0000000..50c36a0 --- /dev/null +++ b/stage03-functions/03-closures/main_test.go @@ -0,0 +1,113 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + add3 := exercise1() + result1 := add3(4) + if result1 != 12 { + t.Errorf("期望 12, 实际: %d", result1) + } + result2 := add3(5) + if result2 != 15 { + t.Errorf("期望 15, 实际: %d", result2) + } +} + +func TestExercise2(t *testing.T) { + counter := exercise2() + result1 := counter() + if result1 != 1 { + t.Errorf("期望 1, 实际: %d", result1) + } + result2 := counter() + if result2 != 2 { + t.Errorf("期望 2, 实际: %d", result2) + } + result3 := counter() + if result3 != 3 { + t.Errorf("期望 3, 实际: %d", result3) + } +} + +func TestExercise3(t *testing.T) { + fib := exercise3() + expected := []int{0, 1, 1, 2, 3} + for i, exp := range expected { + result := fib() + if result != exp { + t.Errorf("第%d个斐波那契数期望 %d, 实际: %d", i+1, exp, result) + } + } +} + +func TestExercise4(t *testing.T) { + inRange := exercise4() + if !inRange(15) { + t.Errorf("15应该在范围内") + } + if inRange(5) { + t.Errorf("5不应该在范围内") + } + if !inRange(20) { + t.Errorf("20应该在范围内") + } + if inRange(21) { + t.Errorf("21不应该在范围内") + } +} + +func TestExercise5(t *testing.T) { + accumulator := exercise5(100) + result1 := accumulator(10) + if result1 != 110 { + t.Errorf("期望 110, 实际: %d", result1) + } + result2 := accumulator(20) + if result2 != 130 { + t.Errorf("期望 130, 实际: %d", result2) + } + result3 := accumulator(30) + if result3 != 160 { + t.Errorf("期望 160, 实际: %d", result3) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 闭包 练习 ===", + "练习1:基础闭包", + "练习2:闭包状态保持", + "练习3:斐波那契数列闭包", + "练习4:闭包配置", + "练习5:闭包累加器", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage03-functions/04-higher-order/main.go b/stage03-functions/04-higher-order/main.go new file mode 100644 index 0000000..b82a42c --- /dev/null +++ b/stage03-functions/04-higher-order/main.go @@ -0,0 +1,120 @@ +package main + +import "fmt" + +func exercise1(apply func(int) int, value int) int { + fmt.Println("=== 练习1:函数作为参数 ===") + + // 接收一个函数apply和一个值value + // 调用apply函数处理value并返回结果 + + result := ____ + + return ____ +} + +func exercise2() func(int) int { + fmt.Println("=== 练习2:函数作为返回值 ===") + + // 返回一个函数,该函数将输入值乘以2 + // 提示:返回一个匿名函数 + + return ____ +} + +func exercise3(vals []int, filter func(int) bool) []int { + fmt.Println("=== 练习3:过滤器函数 ===") + + // 接收一个int切片和一个过滤函数 + // 返回过滤后的切片(只保留满足条件的元素) + + var result ____ + + for _, val := range ____ { + if ____(val) { + result = append(result, ____) + } + } + + return ____ +} + +func exercise4(vals []int, mapper func(int) int) []int { + fmt.Println("=== 练习4:映射函数 ===") + + // 接收一个int切片和一个映射函数 + // 返回映射后的切片(每个元素都经过mapper函数处理) + + result := make([]int, ____) + + for i, val := range ____ { + result[i] = ____(val) + } + + return ____ +} + +func exercise5(vals []int, reducer func(int, int) int, initial int) int { + fmt.Println("=== 练习5:归约函数 ===") + + // 接收一个int切片、一个归约函数和初始值 + // 返回归约结果 + // 例如:对切片求和、求积等 + + result := ____ + + for _, val := range ____ { + result = ____(result, val) + } + + return ____ +} + +func exercise6() int { + fmt.Println("=== 练习6:函数组合 ===") + + // 组合两个函数 + // square函数:计算平方 + // double函数:计算双倍 + // 返回 double(square(3)) 的结果 + // 即:先平方(9),再双倍(18) + + square := func(n int) int { return ____ } + double := func(n int) int { return ____ } + + compose := func(f, g func(int) int, x int) int { + return ____(____(x)) + } + + return compose(____, ____, 3) +} + +func main() { + fmt.Println("=== 高阶函数 练习 ===\n") + + 平方 := func(n int) int { return n * n } + result1 := exercise1(平方, 4) + fmt.Printf("练习1结果: %d\n", result1) + + double := exercise2() + result2 := double(5) + fmt.Printf("练习2结果: %d\n", result2) + + nums1 := []int{1, 2, 3, 4, 5, 6} + isEven := func(n int) bool { return n%2 == 0 } + result3 := exercise3(nums1, isEven) + fmt.Printf("练习3结果: %v\n", result3) + + nums2 := []int{1, 2, 3, 4, 5} + multiplyBy3 := func(n int) int { return n * 3 } + result4 := exercise4(nums2, multiplyBy3) + fmt.Printf("练习4结果: %v\n", result4) + + nums3 := []int{1, 2, 3, 4, 5} + sum := func(acc, val int) int { return acc + val } + result5 := exercise5(nums3, sum, 0) + fmt.Printf("练习5结果: %d\n", result5) + + result6 := exercise6() + fmt.Printf("练习6结果: %d\n", result6) +} diff --git a/stage03-functions/04-higher-order/main_test.go b/stage03-functions/04-higher-order/main_test.go new file mode 100644 index 0000000..e0e07c5 --- /dev/null +++ b/stage03-functions/04-higher-order/main_test.go @@ -0,0 +1,117 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + square := func(n int) int { return n * n } + result := exercise1(square, 4) + if result != 16 { + t.Errorf("期望 16, 实际: %d", result) + } +} + +func TestExercise2(t *testing.T) { + double := exercise2() + result := double(5) + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestExercise3(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6} + isEven := func(n int) bool { return n%2 == 0 } + result := exercise3(nums, isEven) + + if len(result) != 3 { + t.Errorf("期望长度 3, 实际: %d", len(result)) + } + + expected := []int{2, 4, 6} + for i, val := range expected { + if result[i] != val { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, val, result[i]) + } + } +} + +func TestExercise4(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + multiplyBy3 := func(n int) int { return n * 3 } + result := exercise4(nums, multiplyBy3) + + if len(result) != 5 { + t.Errorf("期望长度 5, 实际: %d", len(result)) + } + + expected := []int{3, 6, 9, 12, 15} + for i, val := range expected { + if result[i] != val { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, val, result[i]) + } + } +} + +func TestExercise5(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + sum := func(acc, val int) int { return acc + val } + result := exercise5(nums, sum, 0) + + if result != 15 { + t.Errorf("期望 15, 实际: %d", result) + } + + product := func(acc, val int) int { return acc * val } + result = exercise5(nums, product, 1) + + if result != 120 { + t.Errorf("期望 120, 实际: %d", result) + } +} + +func TestExercise6(t *testing.T) { + result := exercise6() + if result != 18 { + t.Errorf("期望 18, 实际: %d", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 高阶函数 练习 ===", + "练习1:函数作为参数", + "练习2:函数作为返回值", + "练习3:过滤器函数", + "练习4:映射函数", + "练习5:归约函数", + "练习6:函数组合", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage04-composite-types/01-arrays/README.md b/stage04-composite-types/01-arrays/README.md new file mode 100644 index 0000000..66bdb66 --- /dev/null +++ b/stage04-composite-types/01-arrays/README.md @@ -0,0 +1,44 @@ +# 01-arrays - 数组 + +## 知识点讲解 + +### 数组定义 + +```go +var arr1 [5]int // 声明一个长度为5的int数组 +var arr2 = [5]int{1, 2, 3, 4, 5} // 声明并初始化 +arr3 := [5]int{1, 2, 3} // 部分初始化,其余为0 +arr4 := [...]int{1, 2, 3, 4, 5} // 让编译器推断长度 +``` + +### 访问和修改 + +```go +arr := [3]int{10, 20, 30} +fmt.Println(arr[0]) // 输出: 10 +arr[1] = 100 // 修改元素 +``` + +### 数组特点 + +- **固定长度**:创建后长度不可变 +- **值类型**:数组赋值会拷贝整个数组 +- **相同类型**:必须指定长度和元素类型 +- **内存连续**:元素在内存中连续存储 + +### 重要提示 + +- Go中数组长度是类型的一部分,`[3]int` 和 `[4]int` 是不同的类型 +- 数组作为参数传递时会拷贝整个数组,而不是引用 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握数组的声明和初始化 +- ✅ 学会数组的访问和修改 +- ✅ 理解数组的值类型特性 +- ✅ 了解数组的遍历方式 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage04-composite-types/01-arrays/main.go b/stage04-composite-types/01-arrays/main.go new file mode 100644 index 0000000..b7424f2 --- /dev/null +++ b/stage04-composite-types/01-arrays/main.go @@ -0,0 +1,95 @@ +package main + +import "fmt" + +func exercise1() [5]int { + fmt.Println("=== 练习1:数组声明与初始化 ===") + + // 声明一个长度为5的int数组 + // 初始化所有元素为10 + var arr ____ + + for i := 0; i < ____(arr); i++ { + arr[i] = ____ + } + + return ____ +} + +func exercise2() string { + fmt.Println("=== 练习2:数组访问与修改 ===") + + // 修改数组元素并返回修改后的字符串表示 + fruits := [4]string{"苹果", "香蕉", "橙子", "葡萄"} + + fruits[____] = "芒果" + + fmt.Printf("修改后的水果: %s\n", fruits) + + return "芒果" +} + +func exercise3() int { + fmt.Println("=== 练习3:数组求和 ===") + + // 计算数组所有元素的和 + numbers := [5]int{10, 20, 30, 40, 50} + + sum := ____ + + for i := 0; i < ____(numbers); i++ { + sum += ____ + } + + return ____ +} + +func exercise4() [3]int { + fmt.Println("=== 练习4:数组拷贝 ===") + + // 创建一个数组并拷贝到另一个数组 + // 修改原数组,观察拷贝的数组是否变化 + arr1 := [3]int{1, 2, 3} + + arr2 := ____ + + arr1[0] = 99 + + return ____ +} + +func exercise5() int { + fmt.Println("=== 练习5:二维数组 ===") + + // 计算二维数组的和 + matrix := [3][2]int{{1, 2}, {3, 4}, {5, 6}} + + sum := ____ + + for i := 0; i < ____(matrix); i++ { + for j := 0; j < ____(matrix[i]); j++ { + sum += ____ + } + } + + return ____ +} + +func main() { + fmt.Println("=== 数组 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %v\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %s\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %d\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %v\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %d\n", result5) +} diff --git a/stage04-composite-types/01-arrays/main_test.go b/stage04-composite-types/01-arrays/main_test.go new file mode 100644 index 0000000..7efa699 --- /dev/null +++ b/stage04-composite-types/01-arrays/main_test.go @@ -0,0 +1,79 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + expected := [5]int{10, 10, 10, 10, 10} + if result != expected { + t.Errorf("期望 %v, 实际: %v", expected, result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != "芒果" { + t.Errorf("期望 '芒果', 实际: %s", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != 150 { + t.Errorf("期望 150, 实际: %d", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + expected := [3]int{1, 2, 3} + if result != expected { + t.Errorf("期望 %v, 实际: %v", expected, result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != 21 { + t.Errorf("期望 21, 实际: %d", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 数组 练习 ===", + "练习1:数组声明与初始化", + "练习2:数组访问与修改", + "练习3:数组求和", + "练习4:数组拷贝", + "练习5:二维数组", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage04-composite-types/02-slices/README.md b/stage04-composite-types/02-slices/README.md new file mode 100644 index 0000000..d167b2a --- /dev/null +++ b/stage04-composite-types/02-slices/README.md @@ -0,0 +1,53 @@ +# 02-slices - 切片操作 + +## 知识点讲解 + +### 切片定义 + +```go +var s1 []int // 声明一个切片(nil切片) +s2 := []int{1, 2, 3, 4, 5} // 声明并初始化 +s3 := make([]int, 5) // 创建长度为5的切片 +s4 := make([]int, 5, 10) // 创建长度为5,容量为10的切片 +``` + +### 从数组或切片创建 + +```go +arr := [5]int{1, 2, 3, 4, 5} +s1 := arr[1:3] // [2, 3] +s2 := arr[:3] // [1, 2, 3] +s3 := arr[2:] // [3, 4, 5] +``` + +### 添加元素 + +```go +s := []int{1, 2, 3} +s = append(s, 4) // [1, 2, 3, 4] +s = append(s, 5, 6) // [1, 2, 3, 4, 5, 6] +``` + +### 切片特性 + +- **动态长度**:可以自动扩容 +- **引用类型**:切片指向底层数组 +- **三个属性**:指针、长度、容量 +- **浅拷贝**:切片赋值共享底层数组 + +### 重要提示 + +- 使用 `append` 时如果超出容量会创建新数组 +- 切片切片操作不会复制元素,只是创建新的视图 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握切片的声明和创建 +- ✅ 学会使用 append 添加元素 +- ✅ 理解切片的动态扩容 +- ✅ 了解切片的底层结构 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage04-composite-types/02-slices/main.go b/stage04-composite-types/02-slices/main.go new file mode 100644 index 0000000..5695374 --- /dev/null +++ b/stage04-composite-types/02-slices/main.go @@ -0,0 +1,96 @@ +package main + +import "fmt" + +func exercise1() []int { + fmt.Println("=== 练习1:切片声明与初始化 ===") + + // 创建一个长度为3的int切片,初始值为[1, 2, 3] + s := make([]int, ____) + s[0], s[1], s[2] = ____, ____, ____ + + return ____ +} + +func exercise2() int { + fmt.Println("=== 练习2:切片追加 ===") + + // 向切片中添加元素并返回新长度 + fruits := []string{"苹果", "香蕉"} + fruits = append(fruits, ____) + fruits = append(fruits, ____, ____) + + return ____(fruits) +} + +func exercise3() []int { + fmt.Println("=== 练习3:切片切片 ===") + + // 从切片中提取子切片 + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + // 提取索引2到6(不包含6) + sub1 := numbers[____:____] + + // 提取前5个元素 + sub2 := numbers[:____] + + // 提取从第5个开始到末尾 + sub3 := numbers[____:] + + fmt.Printf("子切片1: %v\n", sub1) + fmt.Printf("子切片2: %v\n", sub2) + fmt.Printf("子切片3: %v\n", sub3) + + return sub1 +} + +func exercise4() int { + fmt.Println("=== 练习4:切片拷贝 ===") + + // 修改原始切片,观察使用copy复制的切片是否受影响 + s1 := []int{1, 2, 3} + s2 := make([]int, ____(s1)) + + copy(____, s1) + + s1[0] = 99 + + return ____ +} + +func exercise5() []int { + fmt.Println("=== 练习5:切片遍历 ===") + + // 创建一个新切片,包含原切片中所有偶数 + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + var evens ____ + + for _, num := range ____ { + if num%2 == ____ { + evens = append(____, num) + } + } + + return ____ +} + +func main() { + fmt.Println("=== 切片 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %v\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %d\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %v\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %d\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %v\n", result5) +} diff --git a/stage04-composite-types/02-slices/main_test.go b/stage04-composite-types/02-slices/main_test.go new file mode 100644 index 0000000..80dc7e6 --- /dev/null +++ b/stage04-composite-types/02-slices/main_test.go @@ -0,0 +1,95 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + expected := []int{1, 2, 3} + if len(result) != len(expected) { + t.Errorf("期望长度 %d, 实际: %d", len(expected), len(result)) + } + for i, v := range expected { + if result[i] != v { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, v, result[i]) + } + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != 5 { + t.Errorf("期望长度 5, 实际: %d", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + expected := []int{3, 4, 5} + if len(result) != len(expected) { + t.Errorf("期望长度 %d, 实际: %d", len(expected), len(result)) + } + for i, v := range expected { + if result[i] != v { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, v, result[i]) + } + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result != 1 { + t.Errorf("期望 1, 实际: %d", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + expected := []int{2, 4, 6, 8, 10} + if len(result) != len(expected) { + t.Errorf("期望长度 %d, 实际: %d", len(expected), len(result)) + } + for i, v := range expected { + if result[i] != v { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, v, result[i]) + } + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 切片 练习 ===", + "练习1:切片声明与初始化", + "练习2:切片追加", + "练习3:切片切片", + "练习4:切片拷贝", + "练习5:切片遍历", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage04-composite-types/03-maps/README.md b/stage04-composite-types/03-maps/README.md new file mode 100644 index 0000000..5cb97bb --- /dev/null +++ b/stage04-composite-types/03-maps/README.md @@ -0,0 +1,60 @@ +# 03-maps - map使用 + +## 知识点讲解 + +### map定义 + +```go +var m1 map[string]int // 声明一个map(nil map) +m2 := map[string]int{} // 声明并初始化空map +m3 := map[string]int{ // 声明并初始化 + "张三": 90, + "李四": 85, +} +m4 := make(map[string]int) // 创建一个空map +``` + +### 添加和修改 + +```go +m := make(map[string]int) +m["key"] = 100 // 添加元素 +m["key"] = 200 // 修改元素 +``` + +### 读取和删除 + +```go +m := map[string]int{"a": 1, "b": 2} +value := m["a"] // 读取值 +value, exists := m["c"] // 读取值,检查是否存在 +delete(m, "b") // 删除元素 +``` + +### 遍历map + +```go +m := map[string]int{"a": 1, "b": 2, "c": 3} +for key, value := range m { // 遍历map + fmt.Printf("%s: %d\n", key, value) +} +``` + +### map特性 + +- **无序**:map中元素的顺序是不确定的 +- **引用类型**:map赋值共享底层数据结构 +- **零值**:未找到时对应类型的零值 +- **并发安全**:多个goroutine同时读写需要加锁 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握map的声明和初始化 +- ✅ 学会添加、修改、删除元素 +- ✅ 理解如何检查元素是否存在 +- ✅ 掌握map的遍历方式 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage04-composite-types/03-maps/main.go b/stage04-composite-types/03-maps/main.go new file mode 100644 index 0000000..9641c14 --- /dev/null +++ b/stage04-composite-types/03-maps/main.go @@ -0,0 +1,112 @@ +package main + +import "fmt" + +func exercise1() map[string]int { + fmt.Println("=== 练习1:map声明与初始化 ===") + + // 创建一个map,键为string类型,值为int类型 + // 添加"张三": 90, "李四": 85, "王五": 95 + scores := ____ + + scores["张三"] = ____ + scores["李四"] = ____ + scores["王五"] = ____ + + return ____ +} + +func exercise2() string { + fmt.Println("=== 练习2:map读取与检查 ===") + + // 读取map中的值并检查是否存在 + scores := map[string]int{ + "张三": 90, + "李四": 85, + } + + score, exists := ____ + + if exists { + fmt.Printf("李四的分数是: %d\n", score) + } + + return "85" +} + +func exercise3() int { + fmt.Println("=== 练习3:map遍历 ===") + + // 遍历map,计算所有分数的总和 + scores := map[string]int{ + "张三": 90, + "李四": 85, + "王五": 95, + "赵六": 88, + } + + sum := ____ + + for ____, score := range ____ { + sum += ____ + } + + return ____ +} + +func exercise4() map[string]int { + fmt.Println("=== 练习4:map删除元素 ===") + + // 删除map中的元素并返回删除后的map + scores := map[string]int{ + "张三": 90, + "李四": 85, + "王五": 95, + } + + ____(scores, "李四") + + return ____ +} + +func exercise5() []string { + fmt.Println("=== 练习5:map筛选 ===") + + // 筛选出分数大于等于90的学生姓名 + scores := map[string]int{ + "张三": 90, + "李四": 85, + "王五": 95, + "赵六": 88, + "钱七": 92, + } + + var topStudents ____ + + for name, score := range ____ { + if score >= ____ { + topStudents = append(____, ____) + } + } + + return ____ +} + +func main() { + fmt.Println("=== map 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %v\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %s\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %d\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %v\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %v\n", result5) +} diff --git a/stage04-composite-types/03-maps/main_test.go b/stage04-composite-types/03-maps/main_test.go new file mode 100644 index 0000000..467ad68 --- /dev/null +++ b/stage04-composite-types/03-maps/main_test.go @@ -0,0 +1,95 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result["张三"] != 90 || result["李四"] != 85 || result["王五"] != 95 { + t.Errorf("期望值不正确: %v", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != "85" { + t.Errorf("期望 '85', 实际: %s", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != 358 { + t.Errorf("期望 358, 实际: %d", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if _, exists := result["李四"]; exists { + t.Errorf("李四应该已被删除") + } + if result["张三"] != 90 { + t.Errorf("张三的分数应该是 90") + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + expectedCount := 3 + if len(result) != expectedCount { + t.Errorf("期望数量 %d, 实际: %d", expectedCount, len(result)) + } + + expectedNames := map[string]bool{"张三": false, "王五": false, "钱七": false} + for _, name := range result { + if _, found := expectedNames[name]; !found { + t.Errorf("意外的学生名: %s", name) + } + expectedNames[name] = true + } + + for name, found := range expectedNames { + if !found { + t.Errorf("缺少学生名: %s", name) + } + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== map 练习 ===", + "练习1:map声明与初始化", + "练习2:map读取与检查", + "练习3:map遍历", + "练习4:map删除元素", + "练习5:map筛选", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage04-composite-types/04-structs/README.md b/stage04-composite-types/04-structs/README.md new file mode 100644 index 0000000..1ab9446 --- /dev/null +++ b/stage04-composite-types/04-structs/README.md @@ -0,0 +1,80 @@ +# 04-structs - 结构体 + +## 知识点讲解 + +### 结构体定义 + +```go +type Person struct { + Name string + Age int + Address string +} +``` + +### 创建结构体 + +```go +// 方式1:按顺序赋值 +p1 := Person{"张三", 20, "北京"} + +// 方式2:指定字段名(推荐) +p2 := Person{ + Name: "李四", + Age: 25, + Address: "上海", +} + +// 方式3:使用new关键字,返回指针 +p3 := new(Person) +p3.Name = "王五" +p3.Age = 30 +``` + +### 结构体字段 + +```go +p := Person{Name: "张三", Age: 20} +p.Name = "李四" // 修改字段值 +fmt.Println(p.Age) // 访问字段值 +``` + +### 结构体方法 + +```go +func (p Person) Introduce() string { + return fmt.Sprintf("我是%s,今年%d岁", p.Name, p.Age) +} +``` + +### 匿名字段与嵌套 + +```go +type Address struct { + City string + Country string +} + +type Person struct { + Name string + Address Address // 嵌套结构体 +} +``` + +### 重要提示 + +- 结构体字段首字母大写表示公开,小写表示私有 +- 结构体作为函数参数时默认是值传递 +- 使用指针传递可以避免拷贝,提高性能 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握结构体的定义和创建 +- ✅ 学会访问和修改结构体字段 +- ✅ 理解结构体指针的使用 +- ✅ 掌握结构体的方法定义 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage04-composite-types/04-structs/main.go b/stage04-composite-types/04-structs/main.go new file mode 100644 index 0000000..dfd4202 --- /dev/null +++ b/stage04-composite-types/04-structs/main.go @@ -0,0 +1,111 @@ +package main + +import "fmt" + +type Person struct { + Name string + Age int + Score float64 +} + +func exercise1() Person { + fmt.Println("=== 练习1:结构体定义与创建 ===") + + // 创建一个Person结构体实例 + // 名字为"张三", 年龄为20, 分数为90.5 + + var p ____ + + p.Name = ____ + p.Age = ____ + p.Score = ____ + + return ____ +} + +func exercise2() Person { + fmt.Println("=== 练习2:结构体字段修改 ===") + + // 修改结构体字段并返回 + p := Person{Name: "李四", Age: 25, Score: 85.0} + + p.____ = 26 + p.____ = 88.5 + + return ____ +} + +type Book struct { + Title string + Author string + Price float64 +} + +func (b Book) GetInfo() string { + fmt.Println("=== 练习3:结构体方法 ===") + + // 返回书籍的详细信息 + // 格式: "《书名》- 作者, 价格 ¥XX.XX" + return fmt.Sprintf("《%s》-%s, 价格 ¥%.2f", ____, ____, ____) +} + +func exercise4() *Book { + fmt.Println("=== 练习4:结构体指针 ===") + + // 创建一个Book结构体指针 + // 使用new创建,并设置字段的值 + book := ____ + + book.Title = "Go语言圣经" + book.____ = "Alan Donovan" + book.____ = 99.0 + + return ____ +} + +type Student struct { + Name string + Grade int +} + +type Teacher struct { + Name string + Subject string +} + +func exercise5() interface{} { + fmt.Println("=== 练习5:结构体数组 ===") + + // 创建一个结构体切片 + // 包含2个学生和2个老师 + students := []Student{ + {Name: "张三", Grade: 1}, + {Name: ____, Grade: 2}, + } + + teachers := []Teacher{ + {Name: "王老师", Subject: "数学"}, + {Name: ____, Subject: "英语"}, + } + + return append(students, teachers...) +} + +func main() { + fmt.Println("=== 结构体 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %+v\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %+v\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %s\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %+v\n", *result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %v (共%d个)\n", result5, len(result5.([]interface{}))) +} diff --git a/stage04-composite-types/04-structs/main_test.go b/stage04-composite-types/04-structs/main_test.go new file mode 100644 index 0000000..4f00fbc --- /dev/null +++ b/stage04-composite-types/04-structs/main_test.go @@ -0,0 +1,85 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result.Name != "张三" || result.Age != 20 || result.Score != 90.5 { + t.Errorf("期望值不正确: %+v", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result.Name != "李四" || result.Age != 26 || result.Score != 88.5 { + t.Errorf("期望值不正确: %+v", result) + } +} + +func TestExercise3(t *testing.T) { + book := Book{Title: "Go语言实战", Author: "William Kennedy", Price: 59.9} + result := book.GetInfo() + expected := "《Go语言实战》-William Kennedy, 价格 ¥59.90" + if result != expected { + t.Errorf("期望 '%s', 实际: '%s'", expected, result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result.Title != "Go语言圣经" || result.Author != "Alan Donovan" || result.Price != 99.0 { + t.Errorf("期望值不正确: %+v", *result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + slice, ok := result.([]interface{}) + if !ok { + t.Error("返回结果应该是切片") + return + } + + if len(slice) != 4 { + t.Errorf("期望4个元素, 实际: %d", len(slice)) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 结构体 练习 ===", + "练习1:结构体定义与创建", + "练习2:结构体字段修改", + "练习3:结构体方法", + "练习4:结构体指针", + "练习5:结构体数组", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage05-pointers-memory/01-pointers/README.md b/stage05-pointers-memory/01-pointers/README.md new file mode 100644 index 0000000..0781358 --- /dev/null +++ b/stage05-pointers-memory/01-pointers/README.md @@ -0,0 +1,60 @@ +# 01-pointers - 指针基础 + +## 知识点讲解 + +### 指针声明 + +```go +var p *int // 声明一个int类型的指针 +x := 10 +p = &x // p是x的地址 +``` + +### 指针操作 + +```go +x := 10 +p := &x // 取地址 + +fmt.Println(*p) // 解引用,输出10 +*p = 20 // 通过指针修改值 +fmt.Println(x) // 输出20 +``` + +### 指针与函数 + +```go +function modifyValue(p *int) { + *p = 100 // 修改指针指向的值 +} + +x := 10 +modifyValue(&x) // 传递地址 +fmt.Println(x) // 输出100 +``` + +### 零值 + +```go +var p *int // nil指针 +fmt.Println(p == nil) // true +``` + +### 重要提示 + +- Go中有指针但没有指针运算 +- 指针可以用于共享数据、避免拷贝 +- 指针的零值是nil +- 解引用nil指针会panic + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握指针的声明和创建 +- ✅ 学会取地址和解引用操作 +- ✅ 理解指针与函数参数的关系 +- ✅ 了解nil指针的概念 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage05-pointers-memory/01-pointers/main.go b/stage05-pointers-memory/01-pointers/main.go new file mode 100644 index 0000000..9f91c62 --- /dev/null +++ b/stage05-pointers-memory/01-pointers/main.go @@ -0,0 +1,96 @@ +package main + +import "fmt" + +func exercise1() int { + fmt.Println("=== 练习1:指针声明 ===") + + // 声明一个int变量和一个指向它的指针 + var x int = 10 + var p ____ // 声明指针类型 + + p = ____ // p指向x的地址 + + fmt.Printf("x的值: %d, x的地址: %p\n", x, p) + fmt.Printf("*p的值: %d\n", ____) + + return ____ +} + +func exercise2() int { + fmt.Println("=== 练习2:指针解引用 ===") + + // 通过指针修改值 + x := 20 + p := ____ + + ____ = 30 + + fmt.Println("修改后x的值:", x) + + return ____ +} + +func modifyByPointer(p *int) { + // 通过指针修改值 + ____ = 100 +} + +func exercise3() int { + fmt.Println("=== 练习3:指针作为函数参数 ===") + + // 使用指针作为函数参数 + x := 50 + + ____(&x) + + if x != 100 { + fmt.Println("指针参数修改失败!") + } + + return ____ +} + +func exercise4() bool { + fmt.Println("=== 练习4:指针零值 ===") + + // 检查指针的零值 + var p ____ + + fmt.Printf("未初始化的指针: %v, 是否为nil: %t\n", p, ____) + + return ____ +} + +func exercise5() int { + fmt.Println("=== 练习5:指针与数组 ===") + + // 使用指针访问数组元素 + arr := [5]int{10, 20, 30, 40, 50} + + p := ____ + + fmt.Printf("第一个元素: %d\n", ____) + fmt.Printf("第二个元素: %d\n", ____(p+1)) + + return arr[0] +} + +func main() { + fmt.Println("=== 指针 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %d\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %d\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %d\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %t\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %d\n", result5) +} diff --git a/stage05-pointers-memory/01-pointers/main_test.go b/stage05-pointers-memory/01-pointers/main_test.go new file mode 100644 index 0000000..4381655 --- /dev/null +++ b/stage05-pointers-memory/01-pointers/main_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != 30 { + t.Errorf("期望 30, 实际: %d", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != 100 { + t.Errorf("期望 100, 实际: %d", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if !result { + t.Errorf("期望 true, 实际: %t", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 指针 练习 ===", + "练习1:指针声明", + "练习2:指针解引用", + "练习3:指针作为函数参数", + "练习4:指针零值", + "练习5:指针与数组", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage05-pointers-memory/02-new-make/README.md b/stage05-pointers-memory/02-new-make/README.md new file mode 100644 index 0000000..ac469ce --- /dev/null +++ b/stage05-pointers-memory/02-new-make/README.md @@ -0,0 +1,63 @@ +# 02-new-make - new vs make + +## 知识点讲解 + +### new函数 + +```go +p := new(int) // 返回指向int类型零值的指针 +fmt.Println(*p) // 输出: 0 +*p = 10 // 可以通过指针修改值 +``` + +**特点**: +- 返回指向类型的零值指针 +- 适用于所有类型 +- 内存已初始化为零值但不执行构造逻辑 + +### make函数 + +```go +// 创建切片 +s := make([]int, 5) // [0, 0, 0, 0, 0] +s := make([]int, 3, 10) // 长度3, 容量10 + +// 创建map +m := make(map[string]int) +m["key"] = 100 + +// 创建channel +ch := make(chan int) +``` + +**特点**: +- 只用于创建引用类型(slice、map、channel) +- 返回的是初始化后的类型(非指针) +- 会执行类型的初始化逻辑 + +### 主要区别 + +| 特性 | new() | make() | +|------|-------|--------| +| 返回类型 | 指针 | 值 | +| 适用类型 | 所有类型 | slice, map, channel | +| 初始化 | 仅零值 | 完整初始化 | +| 常用场景 | 需要指针时 | 创建引用类型时 | + +### 重要提示 + +- 使用 `new` 创建的指针指向零值 +- 使用 `make` 创建的引用类型可以直接使用 +- 不要用 `new` 创建切片、map或channel + +## 学习目标 + +完成本练习后,你将: +- ✅ 理解new和make的区别 +- ✅ 掌握new的使用场景 +- ✅ 掌握make的使用场景 +- ✅ 学会正确选择new或make + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage05-pointers-memory/02-new-make/main.go b/stage05-pointers-memory/02-new-make/main.go new file mode 100644 index 0000000..0570b9b --- /dev/null +++ b/stage05-pointers-memory/02-new-make/main.go @@ -0,0 +1,102 @@ +package main + +import "fmt" + +func exercise1() int { + fmt.Println("=== 练习1:new创建指针 ===") + + // 使用new创建一个int类型的指针 + p := ____ // new返回指向int零值的指针 + + fmt.Printf("new创建的指针: %v, 解引用值: %d\n", p, ____) + + *p = 10 + + fmt.Printf("修改后的值: %d\n", ____) + + return ____ +} + +func exercise2() []int { + fmt.Println("=== 练习2:make创建切片 ===") + + // 使用make创建一个长度为5的int切片 + s := ____ + + fmt.Printf("创建的切片: %v, 长度: %d\n", s, ____) + + fmt.Printf("切片的第一个元素: %d\n", s[____]) + + return ____ +} + +func exercise3() map[string]int { + fmt.Println("=== 练习3:make创建map ===") + + // 使用make创建一个map + m := ____ + + m["张三"] = 90 + + fmt.Printf("map内容: %v\n", ____) + + return ____ +} + +func exercise4() int { + fmt.Println("=== 练习4:new vs make - 切片 ===") + + // 分别使用new和make创建切片,观察区别 + newSlice := new([]int) // 返回指针,指向nil切片 + makeSlice := make([]int, 3) // 返回切片实例 + + fmt.Printf("new创建的切片: %v, 是否为nil: %t\n", ____, *newSlice == nil) + fmt.Printf("make创建的切片: %v, 是否为nil: %t\n", ____, makeSlice == nil) + + if *newSlice == nil { + *newSlice = make([]int, 3) + ____[0] = 100 + } + + return ____ +} + +func exercise5() string { + fmt.Println("=== 练习5:new vs make - 结构体 ===") + + // 使用new创建结构体指针 + type Person struct { + Name string + Age int + } + + p := ____ + + fmt.Printf("new创建的结构体: %+v\n", ____) + + p.Name = "张三" + p.Age = 20 + + fmt.Printf("修改后的结构体: %+v\n", ____) + + return ____ +} + +func main() { + fmt.Println("=== new vs make 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %d\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %v\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %v\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %d\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %s\n", result5) +} diff --git a/stage05-pointers-memory/02-new-make/main_test.go b/stage05-pointers-memory/02-new-make/main_test.go new file mode 100644 index 0000000..7343152 --- /dev/null +++ b/stage05-pointers-memory/02-new-make/main_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if len(result) != 5 { + t.Errorf("期望长度 5, 实际: %d", len(result)) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if len(result) != 1 { + t.Errorf("期望长度 1, 实际: %d", len(result)) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result != 100 { + t.Errorf("期望 100, 实际: %d", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != "张三" { + t.Errorf("期望 '张三', 实际: %s", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== new vs make 练习 ===", + "练习1:new创建指针", + "练习2:make创建切片", + "练习3:make创建map", + "练习4:new vs make - 切片", + "练习5:new vs make - 结构体", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage05-pointers-memory/03-value-pointer/README.md b/stage05-pointers-memory/03-value-pointer/README.md new file mode 100644 index 0000000..d3960aa --- /dev/null +++ b/stage05-pointers-memory/03-value-pointer/README.md @@ -0,0 +1,75 @@ +# 03-value-pointer - 值传递vs指针传递 + +## 知识点讲解 + +### 值传递 + +```go +func modifyValue(x int) { + x = 100 // 修改的是函数内的副本 +} + +num := 10 +modifyValue(num) +fmt.Println(num) // 输出: 10(原值不变) +``` + +### 指针传递 + +```go +func modifyPointer(p *int) { + *p = 100 // 修改的是指针指向的值 +} + +num := 10 +modifyPointer(&num) +fmt.Println(num) // 输出: 100(原值被修改) +``` + +### 结构体的值传递 + +```go +type Person struct { + Name string + Age int +} + +func modifyPersonValue(p Person) { + p.Name = "李四" // 修改副本 +} + +p := Person{Name: "张三", Age: 20} +modifyPersonValue(p) +fmt.Println(p.Name) // 输出: 张三(原值不变) +``` + +### 结构体的指针传递 + +```go +func modifyPersonPointer(p *Person) { + p.Name = "李四" // 修改原值 +} + +p := Person{Name: "张三", Age: 20} +modifyPersonPointer(&p) +fmt.Println(p.Name) // 输出: 李四(原值被修改) +``` + +### 重要提示 + +- Go中所有参数传递都是值传递 +- 传递指针实际是拷贝指针地址,但通过指针可以修改原值 +- 大结构体使用指针传递可以避免大量拷贝 +- 切片、map等引用类型传递也具有类似指针的行为 + +## 学习目标 + +完成本练习后,你将: +- ✅ 理解值传递和指针传递的区别 +- ✅ 掌握何时使用值传递 +- ✅ 掌握何时使用指针传递 +- ✅ 了解性能方面的考虑 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage05-pointers-memory/03-value-pointer/main.go b/stage05-pointers-memory/03-value-pointer/main.go new file mode 100644 index 0000000..f892598 --- /dev/null +++ b/stage05-pointers-memory/03-value-pointer/main.go @@ -0,0 +1,130 @@ +package main + +import "fmt" + +func modifyByValue(x int) int { + // 值传递:修改函数内的副本 + x = ____ + return ____ +} + +func modifyByPointer(p *int) int { + // 指针传递:修改指针指向的值 + ____ = ____ + return ____ +} + +type Counter struct { + Value int +} + +func (c Counter) Increment() int { + // 值接收者:修改副本 + c.Value += 1 + return ____ +} + +func (c *Counter) IncrementPointer() int { + // 指针接收者:修改原值 + c.Value += ____ + return ____ +} + +func exercise1() int { + fmt.Println("=== 练习1:值传递 ===") + + // 值传递不会修改原值 + num := 10 + + result := modifyByValue(____) + + fmt.Printf("原值: %d, 函数返回值: %d\n", ____, result) + + return ____ +} + +func exercise2() int { + fmt.Println("=== 练习2:指针传递 ===") + + // 指针传递会修改原值 + num := 10 + + result := ____ + + fmt.Printf("原值: %d, 函数返回值: %d\n", ____, result) + + return ____ +} + +func exercise3() int { + fmt.Println("=== 练习3:结构体值传递 ===") + + // 值方法不会修改原结构体 + counter := Counter{Value: 0} + + for i := 0; i < 5; i++ { + counter.____() + } + + fmt.Printf("原结构体值: %d\n", ____) + + return ____ +} + +func exercise4() int { + fmt.Println("=== 练习4:结构体指针传递 ===") + + // 指针方法会修改原结构体 + counter := Counter{Value: 0} + + for i := 0; i < 5; i++ { + counter.____() + } + + fmt.Printf("原结构体值: %d\n", ____) + + return ____ +} + +type SliceContainer struct { + Data []int +} + +func (s *SliceContainer) Add(value int) { + // 指针方法可以修改切片内容 + s.Data = append(____, ____) +} + +func exercise5() []int { + fmt.Println("=== 练习5:切片与结构体 ===") + + // 使用切片的结构体 + container := SliceContainer{Data: []int{}} + + container.Add(10) + container.Add(20) + container.Add(30) + + fmt.Printf("切片内容: %v\n", ____) + + return ____ +} + +func main() { + fmt.Println("=== 值传递 vs 指针传递 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %d\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %d\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %d\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %d\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %v\n", result5) +} diff --git a/stage05-pointers-memory/03-value-pointer/main_test.go b/stage05-pointers-memory/03-value-pointer/main_test.go new file mode 100644 index 0000000..3f6a170 --- /dev/null +++ b/stage05-pointers-memory/03-value-pointer/main_test.go @@ -0,0 +1,83 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != 100 { + t.Errorf("期望 100, 实际: %d", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != 0 { + t.Errorf("期望 0, 实际: %d", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result != 5 { + t.Errorf("期望 5, 实际: %d", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + expected := []int{10, 20, 30} + if len(result) != len(expected) { + t.Errorf("期望长度 %d, 实际: %d", len(expected), len(result)) + } + for i, val := range expected { + if result[i] != val { + t.Errorf("索引 %d 期望 %d, 实际: %d", i, val, result[i]) + } + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 值传递 vs 指针传递 练习 ===", + "练习1:值传递", + "练习2:指针传递", + "练习3:结构体值传递", + "练习4:结构体指针传递", + "练习5:切片与结构体", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage06-oop/01-interfaces/README.md b/stage06-oop/01-interfaces/README.md new file mode 100644 index 0000000..934103b --- /dev/null +++ b/stage06-oop/01-interfaces/README.md @@ -0,0 +1,74 @@ +# 01-interfaces - 接口定义与实现 + +## 知识点讲解 + +### 接口定义 + +```go +type Shape interface { + Area() float64 + Perimeter() float64 +} +``` + +### 接口实现 + +```go +type Rectangle struct { + Width float64 + Height float64 +} + +func (r Rectangle) Area() float64 { + return r.Width * r.Height +} + +func (r Rectangle) Perimeter() float64 { + return 2 * (r.Width + r.Height) +} +``` + +**注意**:Go中接口是隐式实现的,不需要显式声明 + +### 多态 + +```go +func printShapeInfo(s Shape) { + fmt.Printf("面积: %.2f\n", s.Area()) + fmt.Printf("周长: %.2f\n", s.Perimeter()) +} + +rect := Rectangle{Width: 10, Height: 5} +circle := Circle{Radius: 7} + +printShapeInfo(rect) // 可以传递Rectangle +printShapeInfo(circle) // 也可以传递Circle +``` + +### 空接口 + +```go +var any interface{} +any = 10 +any = "hello" +any = []int{1, 2, 3} +``` + +### 重要提示 + +- 接口定义一组方法签名 +- 任何类型只要实现了接口的所有方法,就自动实现了该接口 +- 空接口 `interface{}` 可以持有任何类型的值 +- 接口类型可以包含零个或多个方法 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握接口的定义方式 +- ✅ 学会实现接口 +- ✅ 理解多态的概念 +- ✅ 了解空接口的用法 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage06-oop/01-interfaces/main.go b/stage06-oop/01-interfaces/main.go new file mode 100644 index 0000000..dc4910a --- /dev/null +++ b/stage06-oop/01-interfaces/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "fmt" + "math" +) + +type Shape interface { + Area() float64 + Perimeter() float64 +} + +type Rectangle struct { + Width float64 + Height float64 +} + +type Circle struct { + Radius float64 +} + +func (r Rectangle) Area() float64 { + return ____ +} + +func (r Rectangle) Perimeter() float64 { + return ____ +} + +func (c Circle) Area() float64 { + return math.Pi * ____ * ____ +} + +func (c Circle) Perimeter() float64 { + return 2 * math.Pi * ____ +} + +type ShapePrinter interface { + PrintInfo() +} + +func (r Rectangle) PrintInfo() { + fmt.Printf("矩形 - 宽: %.2f, 高: %.2f\n", ____, ____) +} + +func (c Circle) PrintInfo() { + fmt.Printf("圆形 - 半径: %.2f\n", ____) +} + +func exercise1() float64 { + fmt.Println("=== 练习1:矩形接口实现 ===") + + rect := Rectangle{Width: 10, Height: 5} + + area := ____ + perimeter := ____ + + fmt.Printf("矩形面积: %.2f, 周长: %.2f\n", area, perimeter) + + return ____ +} + +func exercise2() string { + fmt.Println("=== 练习2:圆形接口实现 ===") + + circle := Circle{Radius: 7} + + area := ____ + info := fmt.Sprintf("圆形面积: %.2f", area) + + fmt.Println(____) + + return ____ +} + +func exercise3() string { + fmt.Println("=== 练习3:多态 ===") + + // 定义一个Shape类型的变量,分别指向Rectangle和Circle + var s Shape + + s = ____ + fmt.Printf("形状类型: %T, 面积: %.2f\n", s, s.____()) + + s = ____ + fmt.Printf("形状类型: %T, 面积: %.2f\n", s, s.____()) + + return "多态演示完成" +} + +func exercise4() interface{} { + fmt.Println("=== 练习4:空接口 ===") + + // 空接口可以持有任何类型 + var data interface{} + + data = ____ + fmt.Printf("数据: %v, 类型: %T\n", data, data) + + data = ____ + fmt.Printf("数据: %v, 类型: %T\n", data, data) + + data = ____ + fmt.Printf("数据: %v, 类型: %T\n", data, data) + + return ____ +} + +type Animal interface { + Sound() string +} + +type Dog struct{} + +func (d Dog) Sound() string { + return "汪汪" +} + +type Cat struct{} + +func (c Cat) Sound() string { + return ____ +} + +func exercise5() string { + fmt.Println("=== 练习5:接口组合与使用 ===") + + // 创建Animal切片,包含不同的动物 + animals := []Animal{____, ____} + + for _, animal := range ____ { + fmt.Printf("动物发出的声音: %s\n", animal.____()) + } + + return "动物接口演示完成" +} + +func main() { + fmt.Println("=== 接口 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %.2f\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %s\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %s\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %v (类型: %T)\n", result4, result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %s\n", result5) +} diff --git a/stage06-oop/01-interfaces/main_test.go b/stage06-oop/01-interfaces/main_test.go new file mode 100644 index 0000000..34de861 --- /dev/null +++ b/stage06-oop/01-interfaces/main_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != 50 { + t.Errorf("期望 50, 实际: %f", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if !strings.Contains(result, "圆形面积:") { + t.Errorf("期望包含 '圆形面积:', 实际: %s", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != "多态演示完成" { + t.Errorf("期望 '多态演示完成', 实际: %s", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result == nil { + t.Error("期望非nil值") + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != "动物接口演示完成" { + t.Errorf("期望 '动物接口演示完成', 实际: %s", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 接口 练习 ===", + "练习1:矩形接口实现", + "练习2:圆形接口实现", + "练习3:多态", + "练习4:空接口", + "练习5:接口组合与使用", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage06-oop/02-methods/README.md b/stage06-oop/02-methods/README.md new file mode 100644 index 0000000..81f55f6 --- /dev/null +++ b/stage06-oop/02-methods/README.md @@ -0,0 +1,73 @@ +# 02-methods - 方法定义 + +## 知识点讲解 + +### 方法定义 + +```go +// 值接收者 +func (p Person) GetName() string { + return p.Name +} + +// 指针接收者 +func (p *Person) SetName(name string) { + p.Name = name +} +``` + +### 值接收者 vs 指针接收者 + +```go +type Counter struct { + value int +} + +func (c Counter) GetValue() int { + return c.value // 只读,不修改原值 +} + +func (c *Counter) Increment() { + c.value++ // 值读写,修改原值 +} +``` + +### 方法选择 + +| 场景 | 接收者类型 | +|------|-----------| +| 方法不需要修改接收者 | 值接收者 | +| 方法需要修改接收者 | 指针接收者 | +| 接收者较大,避免拷贝 | 指针接收者 | +| 保持接口兼容性 | 值接收者 | + +### 方法表达式与值 + +```go +c := Counter{value: 10} + +// 方法调用 +c.Increment() + +// 方法表达式 +f := (*Counter).Increment +f(&c) +``` + +### 重要提示 + +- 值接收者可以用于指针类型,反之则需要Go自动转换 +- 方法集规则影响接口实现 +- 选择接收者类型要考虑一致性 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握方法的定义方式 +- ✅ 理解值接收者和指针接收者的区别 +- ✅ 学会为结构体定义方法 +- ✅ 掌握方法的选择原则 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage06-oop/02-methods/main.go b/stage06-oop/02-methods/main.go new file mode 100644 index 0000000..ee9868d --- /dev/null +++ b/stage06-oop/02-methods/main.go @@ -0,0 +1,163 @@ +package main + +import "fmt" + +type Person struct { + Name string + Age int + Email string +} + +func (p Person) GetName() string { + return ____ +} + +func (p Person) GetAge() int { + return ____ +} + +func (p *Person) SetName(name string) { + ____ = ____ +} + +func (p *Person) SetEmail(email string) { + ____ = ____ +} + +func (p Person) GetInfo() string { + return fmt.Sprintf("%s, %d岁, %s", ____, ____, ____) +} + +type Rectangle struct { + Width float64 + Height float64 +} + +func (r Rectangle) Area() float64 { + return ____ +} + +func (r *Rectangle) Resize(width, height float64) { + ____ = ____ + ____ = ____ +} + +type BankAccount struct { + Balance float64 + Owner string +} + +func (b *BankAccount) Deposit(amount float64) { + b.Balance += ____ +} + +func (b *BankAccount) Withdraw(amount float64) bool { + if b.Balance >= amount { + b.Balance -= ____ + return ____ + } + return false +} + +func (b BankAccount) GetBalance() float64 { + return ____ +} + +func exercise1() string { + fmt.Println("=== 练习1:基础方法 ===") + + person := Person{Name: "张三", Age: 25, Email: "zhangsan@example.com"} + + name := ____ + age := ____ + + person.SetName(____) + person.SetEmail(____) + + fmt.Printf("姓名: %s, 年龄: %d\n", ____, person.Age) + + return ____ +} + +func exercise2() float64 { + fmt.Println("=== 练习2:矩形方法 ===") + + rect := Rectangle{Width: 10, Height: 5} + + area := ____ + + fmt.Printf("矩形面积: %.2f\n", area) + + rect.Resize(____, ____) + + newArea := ____ + fmt.Printf("调整后面积: %.2f\n", ____) + + return ____ +} + +func exercise3() float64 { + fmt.Println("=== 练习3:银行账户方法 ===") + + account := BankAccount{Balance: 1000, Owner: "张三"} + + account.____(500) + fmt.Printf("存款后余额: %.2f\n", account.____()) + + success := account.____(300) + if success { + fmt.Printf("取款成功,余额: %.2f\n", account.____()) + } + + return ____ +} + +func exercise4() string { + fmt.Println("=== 练习4:方法链 ===") + + // 使用方法链调用 + person := Person{Name: "李四", Age: 30, Email: "lisi@example.com"} + + person.SetName("李四(更新)").SetEmail("lisi_new@example.com") + + return ____ +} + +func exercise5() int { + fmt.Println("=== 练习5:计算器方法 ===") + + type Calculator struct { + value int + } + + calc := Calculator{value: 0} + + calc.setValue := func(v int) { + calc.value = v + } + + calc.setValue(10) + + fmt.Printf("计算器值: %d\n", calc.value) + + return ____ +} + +func main() { + fmt.Println("=== 方法 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %s\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %.2f\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %.2f\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %s\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %d\n", result5) +} diff --git a/stage06-oop/02-methods/main_test.go b/stage06-oop/02-methods/main_test.go new file mode 100644 index 0000000..d968869 --- /dev/null +++ b/stage06-oop/02-methods/main_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != "张三(更新)" { + t.Errorf("期望 '张三(更新)', 实际: %s", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != 80 { + t.Errorf("期望 80, 实际: %f", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != 1200 { + t.Errorf("期望 1200, 实际: %f", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result != "李四(更新)" { + t.Errorf("期望 '李四(更新)', 实际: %s", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != 10 { + t.Errorf("期望 10, 实际: %d", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 方法 练习 ===", + "练习1:基础方法", + "练习2:矩形方法", + "练习3:银行账户方法", + "练习4:方法链", + "练习5:计算器方法", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +} diff --git a/stage06-oop/03-embedding/README.md b/stage06-oop/03-embedding/README.md new file mode 100644 index 0000000..93efb92 --- /dev/null +++ b/stage06-oop/03-embedding/README.md @@ -0,0 +1,93 @@ +# 03-embedding - 组合与类型断言 + +## 知识点讲解 + +### 结构体嵌入(组合) + +```go +type Address struct { + City string + Street string +} + +type Person struct { + Name string + Address Address // 嵌套结构体 +} + +type Employee struct { + Person // 匿名嵌入 + EmployeeID int +} + +emp := Employee{ + Person: Person{Name: "张三"}, +} +fmt.Println(emp.Name) // 可以直接访问嵌入的字段 +``` + +### 接口嵌入 + +```go +type Reader interface { + Read() string +} + +type Writer interface { + Write(s string) +} + +type ReadWriter interface { + Reader + Writer // 接口组合 +} +``` + +### 类型断言 + +```go +var i interface{} = "hello" + +// 方式1:标准断言 +s := i.(string) + +// 方式2:带检查 +s, ok := i.(string) +if !ok { + fmt.Println("类型断言失败") +} +``` + +### 类型选择 + +```go +func printValue(v interface{}) { + switch v.(type) { + case int: + fmt.Println("整数:", v) + case string: + fmt.Println("字符串:", v) + default: + fmt.Println("未知类型") + } +} +``` + +### 重要提示 + +- Go使用组合而非继承 +- 结构体嵌入可以实现类似继承的效果 +- 类型断言若失败会panic,需谨慎使用 +- 类型选择用于处理多种类型 + +## 学习目标 + +完成本练习后,你将: +- ✅ 掌握结构体嵌入的使用 +- ✅ 理解接口嵌入的概念 +- ✅ 学会使用类型断言 +- ✅ 掌握类型选择的使用 + +## 练习说明 + +打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。 diff --git a/stage06-oop/03-embedding/main.go b/stage06-oop/03-embedding/main.go new file mode 100644 index 0000000..59c670a --- /dev/null +++ b/stage06-oop/03-embedding/main.go @@ -0,0 +1,182 @@ +package main + +import "fmt" + +type Address struct { + City string + Street string +} + +type Person struct { + Name string + Age int + Address Address +} + +type Employee struct { + Person + EmployeeID int + Department string +} + +type Employee2 struct { + ____ + Salary float64 +} + +type Reader interface { + Read() string +} + +type Writer interface { + Write(s string) +} + +type File struct { + Name string + Content string +} + +func (f File) Read() string { + return f.___ +} + +func (f *File) Write(s string) { + f.Content = ____ +} + +type ReadWriter interface { + ____ + ____ +} + +func exercise1() string { + fmt.Println("=== 练习1:结构体嵌入 ===") + + emp := Employee{ + Person: Person{ + Name: "张三", + Age: 30, + }, + EmployeeID: 1001, + Department: "技术部", + } + + fmt.Printf("员工姓名: %s, 年龄: %d\n", ____, emp.___) + + return ____ +} + +func exercise2() string { + fmt.Println("=== 练习2:多层嵌入 ===") + + address := Address{City: "北京", Street: "长安街"} + + emp2 := Employee2{ + Person: Person{ + Name: "李四", + Address: ____, + }, + Salary: 10000.0, + } + + fmt.Printf("员工: %s, 城市: %s\n", emp2.___, emp2.___.City) + + return ____ +} + +func exercise3() string { + fmt.Println("=== 练习3:接口实现 ===") + + file := File{Name: "test.txt", Content: "初始内容"} + + // 使用Read方法 + content := file.____() + fmt.Printf("读取内容: %s\n", content) + + // 使用Write方法 + file.____("新内容") + fmt.Printf("写入后内容: %s\n", file.___) + + return ___ +} + +func exercise4() string { + fmt.Println("=== 练习4:接口组合 ===") + + file := &File{Name: "data.txt", "原始数据"} + + var rw _____ + + rw.Write("修改后的数据") + result := rw.Read() + + fmt.Printf("读写结果: %s\n", result) + + return ____ +} + +func exercise5() string { + fmt.Println("=== 练习5:类型断言 ===") + + var data interface{} = "Hello, Go!" + + // 尝试断言为string + str, ok := data.____ + if ok { + fmt.Printf("类型断言成功: %s\n", ____) + } + + // 尝试断言为int + _, ok = data.____ + if !ok { + fmt.Println("类型断言失败: 不是int类型") + } + + return "类型断言演示完成" +} + +func exercise6() string { + fmt.Println("=== 练习6:类型选择 ===") + + values := []interface{}{10, "hello", 3.14, true} + + for _, v := range ____ { + switch v.(type) { + case int: + fmt.Printf("整数: %d\n", ____) + case string: + fmt.Printf("字符串: %s\n", ____) + case float64: + fmt.Printf("浮点数: %.2f\n", ____) + case bool: + fmt.Printf("布尔值: %t\n", ____) + default: + fmt.Println("未知类型") + } + } + + return "类型选择演示完成" +} + +func main() { + fmt.Println("=== 组合与类型断言 练习 ===\n") + + result1 := exercise1() + fmt.Printf("练习1结果: %s\n", result1) + + result2 := exercise2() + fmt.Printf("练习2结果: %s\n", result2) + + result3 := exercise3() + fmt.Printf("练习3结果: %s\n", result3) + + result4 := exercise4() + fmt.Printf("练习4结果: %s\n", result4) + + result5 := exercise5() + fmt.Printf("练习5结果: %s\n", result5) + + result6 := exercise6() + fmt.Printf("练习6结果: %s\n", result6) +} diff --git a/stage06-oop/03-embedding/main_test.go b/stage06-oop/03-embedding/main_test.go new file mode 100644 index 0000000..bfc3e98 --- /dev/null +++ b/stage06-oop/03-embedding/main_test.go @@ -0,0 +1,85 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func captureOutput(f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + os.Stdout = old + + var buf bytes.Buffer + buf.ReadFrom(r) + return buf.String() +} + +func TestExercise1(t *testing.T) { + result := exercise1() + if result != "张三" { + t.Errorf("期望 '张三', 实际: %s", result) + } +} + +func TestExercise2(t *testing.T) { + result := exercise2() + if result != "李四" { + t.Errorf("期望 '李四', 实际: %s", result) + } +} + +func TestExercise3(t *testing.T) { + result := exercise3() + if result != "新内容" { + t.Errorf("期望 '新内容', 实际: %s", result) + } +} + +func TestExercise4(t *testing.T) { + result := exercise4() + if result != "修改后的数据" { + t.Errorf("期望 '修改后的数据', 实际: %s", result) + } +} + +func TestExercise5(t *testing.T) { + result := exercise5() + if result != "类型断言演示完成" { + t.Errorf("期望 '类型断言演示完成', 实际: %s", result) + } +} + +func TestExercise6(t *testing.T) { + result := exercise6() + if result != "类型选择演示完成" { + t.Errorf("期望 '类型选择演示完成', 实际: %s", result) + } +} + +func TestMainFunction(t *testing.T) { + output := captureOutput(main) + + requiredSections := []string{ + "=== 组合与类型断言 练习 ===", + "练习1:结构体嵌入", + "练习2:多层嵌入", + "练习3:接口实现", + "练习4:接口组合", + "练习5:类型断言", + "练习6:类型选择", + } + + for _, section := range requiredSections { + if !strings.Contains(output, section) { + t.Errorf("期望输出包含 '%s'", section) + } + } +}