2.4【函数特性】Go 函数定义与高级特性

Go 函数定义与数据结构应用

目录

  1. Go 函数

  2. 切片 (Slice)

  3. 映射 (Map)

  4. 结构体 (Struct)


Go 函数

1. 函数基础语法

基本函数定义

函数语法结构
// 完整的函数语法结构
func functionName(parameter1 type1, parameter2 type2, ...) (returnType1, returnType2, ...) {
    // 函数体
    // 局部变量声明
    // 业务逻辑处理
    return value1, value2, ...
}
函数的组成部分详解

1. 关键字 func

// func 是Go语言定义函数的关键字,所有函数都必须以 func 开头
func sayHello() {
    fmt.Println("Hello, World!")
}

2. 函数名命名规则

// 函数名必须以字母或下划线开头,后面可以跟字母、数字或下划线
func calculateSum() int { return 0 }        // 驼峰命名法(推荐)
func calculate_sum() int { return 0 }       // 下划线命名法
func Calculate() int { return 0 }           // 大写开头表示公开函数
func _privateFunc() int { return 0 }        // 下划线开头的私有函数

// 无效的函数名
// func 123func() {}    // 不能以数字开头
// func func-name() {}  // 不能包含连字符

3. 参数详解

// 无参数函数
func getCurrentTime() string {
    return time.Now().Format("2006-01-02 15:04:05")
}

// 单个参数
func square(x int) int {
    return x * x
}

// 多个相同类型参数(简化写法)
func add(a, b int) int {  // a int, b int 的简化写法
    return a + b
}

// 多个不同类型参数
func formatUser(name string, age int, salary float64) string {
    return fmt.Sprintf("Name: %s, Age: %d, Salary: %.2f", name, age, salary)
}

// 参数类型混合
func processData(id int, name string, active bool, scores []int) string {
    status := "inactive"
    if active {
        status = "active"
    }
    return fmt.Sprintf("User %d: %s (%s) - %d scores", id, name, status, len(scores))
}

4. 返回值详解

// 无返回值函数
func printMessage(msg string) {
    fmt.Println(msg)
    // 隐式 return,等价于 return
}

// 单个返回值
func getLength(s string) int {
    return len(s)
}

// 多个返回值(常用于错误处理)
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// 返回不同类型的多个值
func getUserInfo(id int) (string, int, bool, error) {
    // 模拟数据库查询
    if id <= 0 {
        return "", 0, false, errors.New("invalid user ID")
    }
    return "Alice", 25, true, nil  // name, age, active, error
}
实际应用示例

1. 数学计算函数

// 基本算术运算
func add(a, b int) int {
    return a + b
}

func subtract(a, b int) int {
    return a - b
}

func multiply(a, b int) int {
    return a * b
}

func divide(a, b int) (int, int, error) {
    if b == 0 {
        return 0, 0, errors.New("除数不能为零")
    }
    quotient := a / b    // 商
    remainder := a % b   // 余数
    return quotient, remainder, nil
}

// 使用示例
func mathExample() {
    x, y := 10, 3
    
    sum := add(x, y)
    fmt.Printf("%d + %d = %d\n", x, y, sum)
    
    q, r, err := divide(x, y)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Printf("%d ÷ %d = %d 余 %d\n", x, y, q, r)
    }
}

2. 字符串处理函数

// 字符串操作函数
func reverseString(s string) string {
    runes := []rune(s)  // 支持中文字符
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

func countWords(text string) int {
    words := strings.Fields(text)  // 按空白字符分割
    return len(words)
}

func capitalizeFirst(s string) string {
    if len(s) == 0 {
        return s
    }
    return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
}

// 使用示例
func stringExample() {
    text := "hello world 你好世界"
    
    reversed := reverseString(text)
    fmt.Printf("原文: %s\n", text)
    fmt.Printf("反转: %s\n", reversed)
    
    wordCount := countWords(text)
    fmt.Printf("单词数: %d\n", wordCount)
    
    capitalized := capitalizeFirst("go language")
    fmt.Printf("首字母大写: %s\n", capitalized)
}

3. 数据验证函数

// 邮箱验证函数
func isValidEmail(email string) bool {
    // 简单的邮箱格式验证
    if len(email) < 5 {
        return false
    }
    
    atIndex := strings.Index(email, "@")
    dotIndex := strings.LastIndex(email, ".")
    
    return atIndex > 0 && dotIndex > atIndex+1 && dotIndex < len(email)-1
}

// 密码强度验证
func validatePassword(password string) (bool, []string) {
    var errors []string
    isValid := true
    
    if len(password) < 8 {
        errors = append(errors, "密码长度至少8位")
        isValid = false
    }
    
    hasUpper := false
    hasLower := false
    hasDigit := false
    
    for _, char := range password {
        switch {
        case char >= 'A' && char <= 'Z':
            hasUpper = true
        case char >= 'a' && char <= 'z':
            hasLower = true
        case char >= '0' && char <= '9':
            hasDigit = true
        }
    }
    
    if !hasUpper {
        errors = append(errors, "密码必须包含大写字母")
        isValid = false
    }
    if !hasLower {
        errors = append(errors, "密码必须包含小写字母")
        isValid = false
    }
    if !hasDigit {
        errors = append(errors, "密码必须包含数字")
        isValid = false
    }
    
    return isValid, errors
}

// 使用示例
func validationExample() {
    // 邮箱验证
    emails := []string{"test@example.com", "invalid-email", "user@domain.org"}
    for _, email := range emails {
        if isValidEmail(email) {
            fmt.Printf("✓ %s 是有效邮箱\n", email)
        } else {
            fmt.Printf("✗ %s 是无效邮箱\n", email)
        }
    }
    
    // 密码验证
    passwords := []string{"weakpass", "StrongPass123", "short"}
    for _, pwd := range passwords {
        valid, errors := validatePassword(pwd)
        if valid {
            fmt.Printf("✓ 密码 '%s' 强度合格\n", pwd)
        } else {
            fmt.Printf("✗ 密码 '%s' 不合格: %v\n", pwd, errors)
        }
    }
}

4. 工具函数

// 数组/切片工具函数
func findMax(numbers []int) (int, error) {
    if len(numbers) == 0 {
        return 0, errors.New("空切片没有最大值")
    }
    
    max := numbers[0]
    for _, num := range numbers[1:] {
        if num > max {
            max = num
        }
    }
    return max, nil
}

func contains(slice []string, item string) bool {
    for _, s := range slice {
        if s == item {
            return true
        }
    }
    return false
}

func removeDuplicates(slice []int) []int {
    seen := make(map[int]bool)
    result := []int{}
    
    for _, item := range slice {
        if !seen[item] {
            seen[item] = true
            result = append(result, item)
        }
    }
    return result
}

// 使用示例
func utilityExample() {
    numbers := []int{3, 1, 4, 1, 5, 9, 2, 6, 5}
    
    max, err := findMax(numbers)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Printf("最大值: %d\n", max)
    }
    
    fruits := []string{"apple", "banana", "orange"}
    fmt.Printf("是否包含 'banana': %v\n", contains(fruits, "banana"))
    fmt.Printf("是否包含 'grape': %v\n", contains(fruits, "grape"))
    
    unique := removeDuplicates(numbers)
    fmt.Printf("原数组: %v\n", numbers)
    fmt.Printf("去重后: %v\n", unique)
}
函数设计原则

1. 单一职责原则

// 好的设计 - 每个函数只做一件事
func calculateTax(income float64) float64 {
    if income <= 5000 {
        return 0
    }
    return income * 0.1
}

func formatCurrency(amount float64) string {
    return fmt.Sprintf("¥%.2f", amount)
}

// 不好的设计 - 函数做了太多事情
func calculateAndFormatTax(income float64) string {
    var tax float64
    if income <= 5000 {
        tax = 0
    } else {
        tax = income * 0.1
    }
    return fmt.Sprintf("¥%.2f", tax)  // 混合了计算和格式化
}

2. 函数命名最佳实践

// 动词开头,清晰表达函数功能
func calculateAge(birthYear int) int { /* ... */ }      // 计算年龄
func validateInput(data string) bool { /* ... */ }      // 验证输入
func convertToUpper(text string) string { /* ... */ }   // 转换为大写

// 布尔返回值使用 is/has/can 开头
func isEmpty(s string) bool { return len(s) == 0 }
func hasPermission(user User) bool { /* ... */ }
func canAccess(resource string) bool { /* ... */ }

// 获取数据使用 get/fetch 开头
func getUserById(id int) (User, error) { /* ... */ }
func fetchUserData(id int) ([]byte, error) { /* ... */ }

多返回值函数

// 返回多个值
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

// 使用示例
result, err := divide(10, 2)
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Println("Result:", result)
}

命名返回值与非命名返回值

非命名返回值(普通返回值)
// 非命名返回值 - 必须在 return 语句中指定返回的值
func calculate(a, b int) (int, int, error) {
    if b == 0 {
        return 0, 0, fmt.Errorf("division by zero")
    }
    sum := a + b
    quotient := a / b
    return sum, quotient, nil // 必须显式返回所有值
}

// 使用示例
sum, quotient, err := calculate(10, 2)
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Printf("Sum: %d, Quotient: %d\n", sum, quotient)
}
命名返回值
// 命名返回值 - 在函数签名中为返回值命名
func rectangle(length, width float64) (area, perimeter float64) {
    area = length * width
    perimeter = 2 * (length + width)
    return // 空的 return 语句,自动返回命名的返回值
}

// 也可以显式返回,即使使用了命名返回值
func rectangleExplicit(length, width float64) (area, perimeter float64) {
    area = length * width
    perimeter = 2 * (length + width)
    return area, perimeter // 显式返回
}
两种方式的对比

1. 语法差异

// 非命名返回值
func add(a, b int) int {
    result := a + b
    return result // 必须指定返回值
}

// 命名返回值
func addNamed(a, b int) (result int) {
    result = a + b
    return // 可以使用空 return
}

2. 复杂函数中的应用

// 非命名返回值 - 处理用户登录
func loginUser(username, password string) (string, bool, error) {
    if username == "" {
        return "", false, fmt.Errorf("username cannot be empty")
    }
    if password == "" {
        return "", false, fmt.Errorf("password cannot be empty")
    }
    
    // 模拟验证逻辑
    if username == "admin" && password == "123456" {
        token := "jwt_token_example"
        return token, true, nil
    }
    
    return "", false, fmt.Errorf("invalid credentials")
}

// 命名返回值 - 处理用户登录
func loginUserNamed(username, password string) (token string, success bool, err error) {
    if username == "" {
        err = fmt.Errorf("username cannot be empty")
        return // 相当于 return "", false, err
    }
    if password == "" {
        err = fmt.Errorf("password cannot be empty")
        return
    }
    
    // 模拟验证逻辑
    if username == "admin" && password == "123456" {
        token = "jwt_token_example"
        success = true
        return // 相当于 return token, true, nil
    }
    
    err = fmt.Errorf("invalid credentials")
    return // 相当于 return "", false, err
}

3. 在 defer 中修改返回值

// 命名返回值的特殊用法 - defer 可以修改返回值
func processFile(filename string) (result string, err error) {
    defer func() {
        if err != nil {
            // 在 defer 中可以修改命名返回值
            result = "处理失败"
            err = fmt.Errorf("文件处理错误: %v", err)
        }
    }()
    
    if filename == "" {
        return "", fmt.Errorf("文件名不能为空")
    }
    
    // 模拟文件处理
    result = "处理成功: " + filename
    return
}

// 非命名返回值无法在 defer 中直接修改
func processFileRegular(filename string) (string, error) {
    var result string
    var err error
    
    defer func() {
        if err != nil {
            // 这里修改 result 和 err 不会影响函数返回值
            result = "处理失败" // 这个修改不会生效
        }
    }()
    
    if filename == "" {
        return "", fmt.Errorf("文件名不能为空")
    }
    
    result = "处理成功: " + filename
    return result, nil
}
使用建议

何时使用命名返回值:

  • 函数逻辑复杂,有多个出口点

  • 需要在 defer 中修改返回值

  • 返回值含义需要更清晰的文档说明

  • 函数较长,命名有助于提高可读性

何时使用非命名返回值:

  • 简单函数,逻辑清晰

  • 返回值类型已经足够说明含义

  • 不需要在 defer 中修改返回值

  • 遵循团队代码风格约定

// 推荐:简单函数使用非命名返回值
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

// 推荐:复杂函数使用命名返回值
func parseConfig(configPath string) (config *Config, warnings []string, err error) {
    defer func() {
        if err != nil {
            config = nil // 确保错误时返回 nil
        }
    }()
    
    // 复杂的配置解析逻辑...
    return
}

2. 函数高级特性

可变参数函数

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

// 使用示例
fmt.Println(sum(1, 2, 3, 4, 5)) // 输出: 15

函数作为参数

func apply(fn func(int, int) int, a, b int) int {
    return fn(a, b)
}

func multiply(x, y int) int {
    return x * y
}

// 使用示例
result := apply(multiply, 3, 4) // 输出: 12

匿名函数和闭包

func main() {
    // 匿名函数
    square := func(x int) int {
        return x * x
    }
    fmt.Println(square(5)) // 输出: 25

    // 闭包示例
    counter := func() func() int {
        count := 0
        return func() int {
            count++
            return count
        }
    }()
    
    fmt.Println(counter()) // 输出: 1
    fmt.Println(counter()) // 输出: 2
}
作者:admin  创建时间:2025-08-19 00:08
最后编辑:admin  更新时间:2025-09-21 19:50