phase1-basics.md 23 KB

第一阶段:Go语言基础语法

学习目标

  • 掌握Go语言基本语法和编程范式
  • 理解Go的包管理和项目结构
  • 能够编写简单的Go程序

详细学习内容

1.1 Go语言简介

  • Go语言历史与设计哲学
  • Go语言特性(垃圾回收、并发支持等)
  • Go与其他语言的比较

1.2 开发环境搭建

  • Go安装与配置
  • GOPATH与GOMODULE
  • 开发工具配置(VS Code/GoLand)

1.3 基础语法

// 包声明
package main

// 导入包
import "fmt"

// 主函数
func main() {
    fmt.Println("Hello, World!")
}

1.4 数据类型

  • 基本类型:int, float, bool, string
  • 复合类型:数组、切片、映射
  • 类型转换与类型推断
  • 指针类型基础
  • 自定义类型与类型别名

基本类型示例

package main

import "fmt"

func main() {
    // 数值类型
    var i int = 42
    var f float64 = 3.14
    var b bool = true
    var s string = "Go语言"
    
    fmt.Printf("int: %d, float64: %f, bool: %t, string: %s\n", i, f, b, s)
    
    // 类型转换
    var x int = 10
    var y float64 = float64(x)
    fmt.Printf("x: %d, y: %f\n", x, y)
}

复合类型示例

package main

import "fmt"

func main() {
    // 数组(固定长度)
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    fmt.Println("数组:", arr)
    
    // 切片(动态长度)
    var slice []int = []int{1, 2, 3}
    slice = append(slice, 4) // 添加元素
    fmt.Println("切片:", slice)
    
    // 映射(键值对)
    var m map[string]int = make(map[string]int)
    m["one"] = 1
    m["two"] = 2
    fmt.Println("映射:", m)
}

1.5 变量与常量

package main

import "fmt"

func main() {
    // 变量声明
    var name string = "Go"
    var age = 25
    score := 95.5
    
    fmt.Printf("name: %s, age: %d, score: %f\n", name, age, score)
    
    // 常量
    const PI = 3.14159
    const (
        StatusOK = 200
        StatusNotFound = 404
    )
    
    fmt.Printf("PI: %f, StatusOK: %d, StatusNotFound: %d\n", PI, StatusOK, StatusNotFound)
}

1.6 控制结构

  • if/else条件判断
  • for循环(只有for一种循环)
  • switch多路选择
  • defer延迟执行
  • goto与标签(了解即可)

条件语句示例

package main

import "fmt"

func main() {
    // if/else
    age := 20
    if age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年人")
    }

    // if with initialization
    if num := 10; num%2 == 0 {
        fmt.Println(num, "是偶数")
    }
}

循环语句示例

package main

import "fmt"

func main() {
    // 基本for循环
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }

    // 类似while的for循环
    sum := 1
    for sum < 100 {
        sum += sum
        fmt.Println("sum:", sum)
    }

    // 无限循环(带退出条件)
    count := 0
    for {
        count++
        if count >= 3 {
            break
        }
        fmt.Println("无限循环:", count)
    }

    // 遍历切片
    slice := []string{"a", "b", "c"}
    for index, value := range slice {
        fmt.Println(index, value)
    }
}

Switch语句示例

package main

import (
    "fmt"
    "runtime"
)

func main() {
    // 基本switch
    switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("macOS")
    case "linux":
        fmt.Println("Linux")
    case "windows":
        fmt.Println("Windows")
    default:
        fmt.Printf("%s.\n", os)
    }

    // 无表达式的switch(类似if/else链)
    hour := 14
    switch {
    case hour < 12:
        fmt.Println("上午")
    case hour < 18:
        fmt.Println("下午")
    default:
        fmt.Println("晚上")
    }
}

Defer示例

package main

import "fmt"

func main() {
    // defer按照后进先出(LIFO)顺序执行
    defer fmt.Println("世界")
    defer fmt.Println("你好")
    
    fmt.Println("开始")
    // 输出顺序:开始、你好、世界
}

1.7 函数

  • 函数定义与调用
  • 多返回值
  • 匿名函数与闭包
  • 函数作为参数
  • 可变参数函数
  • 递归函数

函数定义示例

package main

import "fmt"

// 基本函数
func add(x, y int) int {
    return x + y
}

// 多返回值
func swap(x, y string) (string, string) {
    return y, x
}

// 命名返回值
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // 直接返回命名的变量
}

func main() {
    // 测试基本函数
    result := add(3, 5)
    fmt.Println("3 + 5 =", result)
    
    // 测试多返回值
    a, b := swap("hello", "world")
    fmt.Println("交换后:", a, b)
    
    // 测试命名返回值
    x, y := split(17)
    fmt.Println("分割结果:", x, y)
}

可变参数函数

package main

import "fmt"

func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

func main() {
    // 调用
    fmt.Println(sum(1, 2))      // 3
    fmt.Println(sum(1, 2, 3))   // 6
    fmt.Println(sum(1, 2, 3, 4, 5)) // 15
}

闭包示例

package main

import "fmt"

// 闭包函数
func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

func main() {
    // 使用
    pos, neg := adder(), adder()
    for i := 0; i < 5; i++ {
        fmt.Println(
            pos(i),     // 0 1 3 6 10...
            neg(-2*i),  // 0 -2 -6 -12 -20...
        )
    }
}

递归函数

package main

import "fmt"

// 阶乘计算
func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

// 斐波那契数列
func fib(n int) int {
    if n < 2 {
        return n
    }
    return fib(n-1) + fib(n-2)
}

func main() {
    // 测试阶乘
    fmt.Println("5! =", factorial(5))
    
    // 测试斐波那契数列
    for i := 0; i < 10; i++ {
        fmt.Printf("fib(%d) = %d\n", i, fib(i))
    }
}

练习项目

项目1:基础计算器

package main

import (
    "fmt"
    "strconv"
    "strings"
)

// 实现加减乘除运算
func calculator(a, b float64, operator string) (float64, error) {
    switch operator {
    case "+":
        return a + b, nil
    case "-":
        return a - b, nil
    case "*":
        return a * b, nil
    case "/":
        if b == 0 {
            return 0, fmt.Errorf("除数不能为零")
        }
        return a / b, nil
    default:
        return 0, fmt.Errorf("不支持的运算符: %s", operator)
    }
}

// 扩展功能:支持表达式解析
func evaluateExpression(expr string) (float64, error) {
    // 简单实现:只支持两个数的运算
    expr = strings.ReplaceAll(expr, " ", "")
    
    // 查找运算符
    var operator string
    var index int
    for i, c := range expr {
        if c == '+' || c == '-' || c == '*' || c == '/' {
            operator = string(c)
            index = i
            break
        }
    }
    
    if operator == "" {
        return 0, fmt.Errorf("无效的表达式")
    }
    
    // 提取两个数字
    num1Str := expr[:index]
    num2Str := expr[index+1:]
    
    num1, err := strconv.ParseFloat(num1Str, 64)
    if err != nil {
        return 0, fmt.Errorf("无效的数字: %s", num1Str)
    }
    
    num2, err := strconv.ParseFloat(num2Str, 64)
    if err != nil {
        return 0, fmt.Errorf("无效的数字: %s", num2Str)
    }
    
    return calculator(num1, num2, operator)
}

func main() {
    // 测试基本计算
    result, err := calculator(10, 5, "+")
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("10 + 5 =", result)
    }
    
    // 测试表达式解析
    expr := "3.5 * 2"
    result, err = evaluateExpression(expr)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Printf("%s = %.2f\n", expr, result)
    }
}

项目2:温度转换器

package main

import (
    "fmt"
    "strings"
)

// 摄氏温度与华氏温度转换
func celsiusToFahrenheit(c float64) float64 {
    return c*9/5 + 32
}

func fahrenheitToCelsius(f float64) float64 {
    return (f - 32) * 5 / 9
}

// 摄氏温度与开尔文温度转换
func celsiusToKelvin(c float64) float64 {
    return c + 273.15
}

func kelvinToCelsius(k float64) float64 {
    return k - 273.15
}

// 扩展:支持多种温度单位转换
type Temperature struct {
    Value float64
    Unit  string // "C", "F", "K"
}

func ConvertTemperature(temp Temperature, targetUnit string) (Temperature, error) {
    // 先转换为摄氏温度
    var celsius float64
    switch strings.ToUpper(temp.Unit) {
    case "C":
        celsius = temp.Value
    case "F":
        celsius = fahrenheitToCelsius(temp.Value)
    case "K":
        celsius = kelvinToCelsius(temp.Value)
    default:
        return Temperature{}, fmt.Errorf("不支持的温度单位: %s", temp.Unit)
    }
    
    // 从摄氏温度转换为目标单位
    var result float64
    switch strings.ToUpper(targetUnit) {
    case "C":
        result = celsius
    case "F":
        result = celsiusToFahrenheit(celsius)
    case "K":
        result = celsiusToKelvin(celsius)
    default:
        return Temperature{}, fmt.Errorf("不支持的目标温度单位: %s", targetUnit)
    }
    
    return Temperature{Value: result, Unit: strings.ToUpper(targetUnit)}, nil
}

func main() {
    // 测试基本转换
    c := 25.0
    f := celsiusToFahrenheit(c)
    fmt.Printf("%.2f°C = %.2f°F\n", c, f)
    
    f = 77.0
    c = fahrenheitToCelsius(f)
    fmt.Printf("%.2f°F = %.2f°C\n", f, c)
    
    // 测试多种温度单位转换
    temp := Temperature{Value: 100, Unit: "C"}
    
    targets := []string{"F", "K"}
    for _, target := range targets {
        converted, err := ConvertTemperature(temp, target)
        if err != nil {
            fmt.Println("错误:", err)
        } else {
            fmt.Printf("%.2f%s = %.2f%s\n", temp.Value, temp.Unit, converted.Value, converted.Unit)
        }
    }
}

项目3:文本统计工具

package main

import (
    "fmt"
    "sort"
    "strings"
)

// 统计文本中的字符数、单词数、行数
func textStats(text string) (chars, words, lines int) {
    chars = len(text)
    words = len(strings.Fields(text))
    lines = strings.Count(text, "\n") + 1
    return
}

// 扩展功能:词频统计
func wordFrequency(text string) map[string]int {
    words := strings.Fields(strings.ToLower(text))
    freq := make(map[string]int)
    for _, word := range words {
        // 去除标点符号
        cleanWord := strings.Trim(word, ".,!?;:\"'")
        if cleanWord != "" {
            freq[cleanWord]++
        }
    }
    return freq
}

// 扩展功能:查找最长单词
func longestWord(text string) string {
    words := strings.Fields(text)
    longest := ""
    for _, word := range words {
        cleanWord := strings.Trim(word, ".,!?;:\"'")
        if len(cleanWord) > len(longest) {
            longest = cleanWord
        }
    }
    return longest
}

// 按频率排序单词
func sortWordsByFrequency(freq map[string]int) []string {
    type wordFreq struct {
        word  string
        count int
    }
    
    var wordFreqs []wordFreq
    for word, count := range freq {
        wordFreqs = append(wordFreqs, wordFreq{word, count})
    }
    
    sort.Slice(wordFreqs, func(i, j int) bool {
        return wordFreqs[i].count > wordFreqs[j].count
    })
    
    var result []string
    for _, wf := range wordFreqs {
        result = append(result, fmt.Sprintf("%s: %d", wf.word, wf.count))
    }
    
    return result
}

func main() {
    text := `Go is an open source programming language that makes it easy to build simple,
reliable, and efficient software. Go is developed by Google.`
    
    // 基本统计
    chars, words, lines := textStats(text)
    fmt.Printf("字符数: %d, 单词数: %d, 行数: %d\n", chars, words, lines)
    
    // 词频统计
    freq := wordFrequency(text)
    fmt.Println("\n词频统计:")
    sortedWords := sortWordsByFrequency(freq)
    for _, wordCount := range sortedWords {
        fmt.Println(wordCount)
    }
    
    // 最长单词
    longest := longestWord(text)
    fmt.Printf("\n最长单词: %s\n", longest)
}

项目4:猜数字游戏

package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "time"
)

func playGuessingGame() {
    // 初始化随机数种子
    rand.Seed(time.Now().UnixNano())
    
    // 生成1-100的随机数
    target := rand.Intn(100) + 1
    var guess int
    attempts := 0
    
    fmt.Println("猜一个1到100之间的数字")
    
    reader := bufio.NewReader(os.Stdin)
    
    for {
        fmt.Print("请输入你的猜测: ")
        input, _ := reader.ReadString('\n')
        input = strings.TrimSpace(input)
        
        num, err := strconv.Atoi(input)
        if err != nil {
            fmt.Println("请输入有效的数字!")
            continue
        }
        
        guess = num
        attempts++
        
        if guess < target {
            fmt.Println("太小了!")
        } else if guess > target {
            fmt.Println("太大了!")
        } else {
            fmt.Printf("恭喜! 你用%d次猜中了数字%d\n", attempts, target)
            break
        }
    }
}

func main() {
    playGuessingGame()
}

项目5:简单通讯录

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

type Contact struct {
    Name    string
    Phone   string
    Email   string
    Address string
}

type AddressBook struct {
    contacts []Contact
}

func (ab *AddressBook) AddContact(contact Contact) {
    ab.contacts = append(ab.contacts, contact)
}

func (ab *AddressBook) FindContact(name string) *Contact {
    for i, contact := range ab.contacts {
        if contact.Name == name {
            return &ab.contacts[i]
        }
    }
    return nil
}

func (ab *AddressBook) DeleteContact(name string) bool {
    for i, contact := range ab.contacts {
        if contact.Name == name {
            ab.contacts = append(ab.contacts[:i], ab.contacts[i+1:]...)
            return true
        }
    }
    return false
}

func (ab *AddressBook) ListContacts() {
    fmt.Println("通讯录:")
    for _, contact := range ab.contacts {
        fmt.Printf("姓名: %s, 电话: %s, 邮箱: %s, 地址: %s\n", 
            contact.Name, contact.Phone, contact.Email, contact.Address)
    }
}

func (ab *AddressBook) UpdateContact(name string, newContact Contact) bool {
    for i, contact := range ab.contacts {
        if contact.Name == name {
            ab.contacts[i] = newContact
            return true
        }
    }
    return false
}

func showMenu() {
    fmt.Println("\n===== 通讯录管理系统 =====")
    fmt.Println("1. 添加联系人")
    fmt.Println("2. 查找联系人")
    fmt.Println("3. 删除联系人")
    fmt.Println("4. 更新联系人")
    fmt.Println("5. 显示所有联系人")
    fmt.Println("6. 退出")
    fmt.Print("请选择操作: ")
}

func main() {
    addressBook := AddressBook{}
    reader := bufio.NewReader(os.Stdin)
    
    for {
        showMenu()
        choice, _ := reader.ReadString('\n')
        choice = strings.TrimSpace(choice)
        
        switch choice {
        case "1":
            fmt.Println("\n--- 添加联系人 ---")
            fmt.Print("姓名: ")
            name, _ := reader.ReadString('\n')
            name = strings.TrimSpace(name)
            
            fmt.Print("电话: ")
            phone, _ := reader.ReadString('\n')
            phone = strings.TrimSpace(phone)
            
            fmt.Print("邮箱: ")
            email, _ := reader.ReadString('\n')
            email = strings.TrimSpace(email)
            
            fmt.Print("地址: ")
            address, _ := reader.ReadString('\n')
            address = strings.TrimSpace(address)
            
            contact := Contact{
                Name:    name,
                Phone:   phone,
                Email:   email,
                Address: address,
            }
            
            addressBook.AddContact(contact)
            fmt.Println("联系人添加成功!")
            
        case "2":
            fmt.Print("\n请输入要查找的联系人姓名: ")
            name, _ := reader.ReadString('\n')
            name = strings.TrimSpace(name)
            
            contact := addressBook.FindContact(name)
            if contact != nil {
                fmt.Printf("找到联系人: 姓名: %s, 电话: %s, 邮箱: %s, 地址: %s\n", 
                    contact.Name, contact.Phone, contact.Email, contact.Address)
            } else {
                fmt.Println("未找到该联系人")
            }
            
        case "3":
            fmt.Print("\n请输入要删除的联系人姓名: ")
            name, _ := reader.ReadString('\n')
            name = strings.TrimSpace(name)
            
            if addressBook.DeleteContact(name) {
                fmt.Println("联系人删除成功!")
            } else {
                fmt.Println("未找到该联系人")
            }
            
        case "4":
            fmt.Print("\n请输入要更新的联系人姓名: ")
            name, _ := reader.ReadString('\n')
            name = strings.TrimSpace(name)
            
            if addressBook.FindContact(name) == nil {
                fmt.Println("未找到该联系人")
                continue
            }
            
            fmt.Println("\n--- 更新联系人信息 ---")
            fmt.Print("新电话: ")
            phone, _ := reader.ReadString('\n')
            phone = strings.TrimSpace(phone)
            
            fmt.Print("新邮箱: ")
            email, _ := reader.ReadString('\n')
            email = strings.TrimSpace(email)
            
            fmt.Print("新地址: ")
            address, _ := reader.ReadString('\n')
            address = strings.TrimSpace(address)
            
            newContact := Contact{
                Name:    name,
                Phone:   phone,
                Email:   email,
                Address: address,
            }
            
            if addressBook.UpdateContact(name, newContact) {
                fmt.Println("联系人更新成功!")
            }
            
        case "5":
            addressBook.ListContacts()
            
        case "6":
            fmt.Println("感谢使用通讯录管理系统,再见!")
            return
            
        default:
            fmt.Println("无效的选择,请重新输入")
        }
    }
}

推荐资源

在线教程

书籍

  • 《Go语言圣经》- 经典入门教材
  • 《Go语言编程》- 实践导向

视频课程

  • Go语言基础入门(慕课网)
  • Go语言核心编程(B站)

评估方式

代码练习评估

  1. 语法正确性:代码能否编译通过
  2. 逻辑完整性:功能是否完整实现
  3. 代码规范:命名规范、注释完整性

项目评估标准

  • 计算器项目:支持4种基本运算,错误处理
  • 温度转换:精确转换,支持双向转换
  • 文本统计:准确统计各类数据

时间安排(1-2周)

  • 第1-3天:环境搭建与基础语法

    • 安装Go开发环境
    • 学习包声明、导入、主函数
    • 编写第一个Go程序
    • 理解Go的项目结构
  • 第4-7天:数据类型与控制结构

    • 掌握基本数据类型和复合类型
    • 学习变量声明与类型转换
    • 练习条件语句和循环
    • 理解switch和defer的使用
  • 第8-10天:函数与项目练习

    • 学习函数定义与调用
    • 掌握多返回值和可变参数
    • 理解闭包和递归
    • 完成计算器和温度转换项目
  • 第11-14天:综合练习与复习

    • 完成文本统计工具
    • 开发猜数字游戏
    • 实现简单通讯录
    • 复习所有知识点并准备进入下一阶段

常见问题

Q: Go的包管理如何使用?

A: 使用go mod init初始化模块,go mod tidy管理依赖。例如:

go mod init myproject
go mod tidy  # 下载并整理依赖
go mod download  # 下载依赖到本地缓存

Q: 为什么Go只有for循环?

A: Go设计哲学强调简洁,for循环足够灵活。Go的for循环可以:

  • 替代传统的for循环
  • 替代while循环
  • 替代do-while循环
  • 遍历数据结构

Q: defer的执行顺序?

A: defer语句按照后进先出(LIFO)的顺序执行。多个defer语句会压入栈中,函数返回时按相反顺序执行。

Q: Go中的nil和空字符串有什么区别?

A: nil表示"无值",适用于指针、切片、映射、函数、接口和通道;空字符串""是一个有效的字符串值,长度为0。

Q: 切片和数组的区别?

A: 数组是固定长度的值类型,切片是动态长度的引用类型。切片底层依赖数组,但可以自动扩容。

Q: 如何理解Go的闭包?

A: 闭包是一个函数值,它引用了函数体之外的变量。该函数可以访问并赋予其引用的变量的值。

Q: Go的错误处理方式?

A: Go使用多返回值处理错误,通常函数返回一个结果和一个error类型值。error为nil表示成功,非nil表示失败。

Q: :=和var声明有什么区别?

A: :=是短变量声明,只能在函数内部使用,自动类型推断;var是完整声明,可在任何地方使用,需要显式指定类型或提供初始值。

Q: Go的命名规范?

A:

  • 公开名称(首字母大写)可被其他包访问
  • 私有名称(首字母小写)只能在包内访问
  • 使用驼峰命名法,不使用下划线
  • 常量通常全大写,用下划线分隔

学习建议

1. 实践为主

  • 每个知识点都要编写代码验证
  • 不要只看不练,动手实践是学习Go的关键
  • 尝试修改示例代码,观察结果变化

2. 循序渐进

  • 先掌握基础语法,再深入特性
  • 不要急于求成,扎实基础更重要
  • 遇到问题及时查阅官方文档

3. 代码规范

  • 养成良好的命名习惯
  • 添加必要的注释
  • 使用gofmt格式化代码

4. 调试技巧

  • 学会使用fmt.Println进行简单调试
  • 了解Go的panic和recover机制
  • 尝试使用Delve等调试工具

自我检测

完成本阶段学习后,尝试回答以下问题:

  1. Go语言的主要特点是什么?
  2. 如何声明和使用不同类型的变量?
  3. for循环有哪些不同的使用方式?
  4. 函数如何返回多个值?
  5. 什么是闭包?它有什么用途?
  6. 切片和数组有什么区别?
  7. defer语句的执行顺序是怎样的?
  8. 如何处理Go中的错误?

下一步

完成本阶段学习后,进入第二阶段:核心概念学习,包括:

  • 指针深入理解
  • 结构体与方法
  • 接口与多态
  • 错误处理机制
  • 并发编程基础