《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(下)

简介: 《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(上)

《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(上)+https://developer.aliyun.com/article/1486982

2.2.4 拓展案例 2:批量处理用户数据

在现实世界的编程任务中,批量处理数据是常见的需求。无论是处理用户信息、订单数据还是日志文件,能够高效地处理大量数据是每个程序员必备的技能。本案例将通过一个具体的例子——批量处理用户数据——来演示如何使用 Go 语言进行批量数据处理。

功能描述

假设我们有一批用户数据,每个用户都有姓名和年龄属性。我们的目标是:

  1. 读取用户数据。
  2. 计算用户的平均年龄。
  3. 找出最年轻和最年长的用户。

实现代码

首先,我们定义一个用户结构体,然后模拟一批用户数据。接着,我们通过遍历这批数据来实现上述功能。

package main
import "fmt"
// User 定义用户结构体
type User struct {
    Name string
    Age  int
}
// 模拟用户数据
var users = []User{
    {"Alice", 30},
    {"Bob", 25},
    {"Carol", 35},
    {"David", 28},
}
func main() {
    var totalAge, maxAge, minAge int
    var youngest, oldest User
    minAge = users[0].Age
    for _, user := range users {
        totalAge += user.Age
        if user.Age > maxAge {
            maxAge = user.Age
            oldest = user
        }
        if user.Age < minAge {
            minAge = user.Age
            youngest = user
        }
    }
    averageAge := float64(totalAge) / float64(len(users))
    fmt.Printf("用户平均年龄: %.2f\n", averageAge)
    fmt.Printf("最年轻的用户: %s (%d岁)\n", youngest.Name, youngest.Age)
    fmt.Printf("最年长的用户: %s (%d岁)\n", oldest.Name, oldest.Age)
}

扩展功能:用户分组

为了进一步提高程序的实用性,我们可以添加一个功能,根据年龄将用户分为不同的年龄组。例如,我们可以定义年轻用户(<30岁)、中年用户(30-50岁)和资深用户(>50岁)。

func groupUsers(users []User) (youngUsers, middleAgedUsers, seniorUsers []User) {
    for _, user := range users {
        switch {
        case user.Age < 30:
            youngUsers = append(youngUsers, user)
        case user.Age <= 50:
            middleAgedUsers = append(middleAgedUsers, user)
        default:
            seniorUsers = append(seniorUsers, user)
        }
    }
    return youngUsers, middleAgedUsers, seniorUsers
}
func main() {
    // 省略上面的平均年龄、最年轻和最年长用户的计算代码
    youngUsers, middleAgedUsers, seniorUsers := groupUsers(users)
    fmt.Printf("年轻用户: %v\n", youngUsers)
    fmt.Printf("中年用户: %v\n", middleAgedUsers)
    fmt.Printf("资深用户: %v\n", seniorUsers)
}

通过这个案例,我们学习了如何在 Go 语言中批量处理和分析数据。这些技能不仅限于处理用户数据,还可以应用于各种场景,比如数据分析、报告生成等。掌握这些基础技能后,你将能够更加自信地处理更复杂的数据处理任务。

2.3 函数定义与使用:Go 语言的超能力

在 Go 语言的奇妙世界里,函数是我们的超能力。它们让我们能够封装代码块,进行复用和模块化管理。想象一下,有了这种超能力,你可以创建一个魔法咒语(函数),在需要的时候唤醒它,执行一些神奇的操作。让我们深入探索这个超能力,看看如何在 Go 中定义和使用函数。

2.3.1 基础知识讲解

函数定义

在 Go 中定义一个函数,你需要使用 func 关键字,后面跟上函数名、参数列表、返回类型和函数体。

func functionName(param1 type1, param2 type2) returnType {
    // 函数体
    return value
}

无返回值函数

如果函数不需要返回任何值,你可以省略返回类型。

func sayHello(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

多返回值

Go 语言的一个独特特性是支持多返回值,这在处理错误或需要返回多个值的情况下非常有用。

func divide(a, b float64) (float64, error) {
    if b == 0.0 {
        return 0.0, fmt.Errorf("除数不能为 0")
    }
    return a / b, nil
}

2.3.2 重点案例:数据过滤器

在数据处理和分析中,我们经常需要从一大堆数据中筛选出符合特定条件的数据。这个过程就像是用一个过滤网,只保留那些有用的信息。通过扩展我们的数据过滤器案例,我们将创建一个更通用的过滤器函数,它不仅可以过滤偶数,还可以根据任何给定的条件进行过滤。这将展示 Go 语言在处理数据时的强大灵活性。

功能描述

  1. 创建一个通用的数据过滤器函数。
  2. 允许用户定义自己的过滤条件。
  3. 应用过滤条件并返回过滤后的数据集。

实现代码

为了实现这个功能,我们将使用 Go 语言的函数类型,允许用户传入自定义的过滤逻辑。

package main
import "fmt"
// FilterFunc 定义了过滤函数的类型
type FilterFunc func(int) bool
// filterNumbers 接受一个整数切片和过滤函数,返回一个新的切片,包含所有符合过滤条件的整数
func filterNumbers(numbers []int, filter FilterFunc) []int {
    var filtered []int
    for _, num := range numbers {
        if filter(num) {
            filtered = append(filtered, num)
        }
    }
    return filtered
}
// 示例过滤条件
func isEven(number int) bool {
    return number%2 == 0
}
func isOdd(number int) bool {
    return number%2 != 0
}
func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 使用 isEven 过滤条件
    evens := filterNumbers(numbers, isEven)
    fmt.Println("偶数:", evens)
    
    // 使用 isOdd 过滤条件
    odds := filterNumbers(numbers, isOdd)
    fmt.Println("奇数:", odds)
}

扩展功能:自定义过滤条件

通过上述实现,我们可以很容易地扩展我们的过滤器来支持更多种类的过滤条件。用户只需要定义符合 FilterFunc 类型的新函数,即可实现新的过滤逻辑。

func greaterThanFive(number int) bool {
    return number > 5
}
func main() {
    // 省略之前的示例
    // 使用 greaterThanFive 过滤条件
    greaterNums := filterNumbers(numbers, greaterThanFive)
    fmt.Println("大于 5 的数:", greaterNums)
}

通过这个案例的扩展,我们展示了如何在 Go 语言中创建灵活且强大的数据处理工具。使用函数作为参数使我们的过滤器函数变得极其通用,能够适应各种不同的数据处理场景。这种模式在 Go 中很常见,它鼓励我们编写更模块化、更可重用的代码。掌握了这种技能,你将能够更有效地处理和分析数据,为解决复杂的问题提供了强大的工具。

2.3.3 拓展案例 1:字符串翻转

字符串翻转是编程中的常见任务,无论是在创建数据处理程序、开发游戏,还是在编写加密算法时,都可能用到这个操作。通过这个案例,我们将创建一个功能更全面的字符串处理工具,它不仅可以翻转字符串,还能进行其他相关操作,如统计字符串长度、判断字符串是否为回文等。

功能描述

  1. 翻转给定的字符串。
  2. 统计字符串中的字符数量。
  3. 判断字符串是否为回文(正读和反读都一样的字符串)。

实现代码

首先,我们实现一个函数来翻转字符串,然后扩展程序以包括其他功能。

package main
import (
    "fmt"
    "unicode/utf8"
)
// reverseString 翻转字符串
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)
}
// isPalindrome 判断字符串是否为回文
func isPalindrome(s string) bool {
    return s == reverseString(s)
}
func main() {
    input := "level"
    reversed := reverseString(input)
    fmt.Printf("原字符串: %s\n", input)
    fmt.Printf("翻转后: %s\n", reversed)
    fmt.Printf("字符数量: %d\n", utf8.RuneCountInString(input))
    if isPalindrome(input) {
        fmt.Println("字符串是回文")
    } else {
        fmt.Println("字符串不是回文")
    }
}

扩展功能:处理多语言文本

在处理包含非ASCII字符的字符串(如中文、日文或特殊符号)时,我们需要考虑Unicode编码。Go语言的unicode/utf8包为此提供了支持。在上述程序中,我们已经使用了utf8.RuneCountInString来正确统计字符数量,这确保了程序可以正确处理多语言文本。

更多字符串操作

Go标准库中的strings包提供了丰富的字符串处理功能,如分割字符串、连接字符串、字符串替换等。通过组合使用这些功能,我们可以创建更加强大和灵活的字符串处理工具。

通过这个案例的扩展,我们学习了如何在Go语言中进行基本的字符串操作,包括翻转字符串、判断回文和正确处理Unicode字符。掌握这些技能后,你将能够更加自如地处理字符串相关的编程任务,为开发复杂的文本处理程序打下坚实的基础。

2.3.4 拓展案例 2:简单的计算器

在开发过程中,创建一个简单的计算器程序是一个很好的实践,它能帮助我们理解函数的使用、条件判断以及用户输入的处理。通过这个案例,我们将开发一个更完整的命令行计算器,它不仅支持基本的数学运算,还包括一些额外的功能,比如计算平方根和幂运算。

功能描述

  1. 支持加、减、乘、除四种基本运算。
  2. 扩展功能以支持计算平方根和幂运算。
  3. 提供用户友好的命令行交互界面。

实现代码

首先,我们定义一个函数来处理基本的数学运算,并扩展程序以包括平方根和幂运算的功能。

package main
import (
    "bufio"
    "fmt"
    "math"
    "os"
    "strconv"
    "strings"
)
// calculate 执行数学运算
func calculate(a float64, b float64, operation string) (float64, error) {
    switch operation {
    case "+":
        return a + b, nil
    case "-":
        return a - b, nil
    case "*":
        return a * b, nil
    case "/":
        if b == 0 {
            return 0, fmt.Errorf("除数不能为 0")
        }
        return a / b, nil
    case "^":
        return math.Pow(a, b), nil
    case "sqrt":
        if a < 0 {
            return 0, fmt.Errorf("被开方数不能为负")
        }
        return math.Sqrt(a), nil
    default:
        return 0, fmt.Errorf("未知的运算符")
    }
}
func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Println("简单计算器")
    fmt.Print("请输入表达式 (例如 2 + 3 或 sqrt 4): ")
    input, _ := reader.ReadString('\n')
    parts := strings.Fields(input)
    if len(parts) < 2 {
        fmt.Println("输入格式错误")
        return
    }
    operation := parts[1]
    a, err := strconv.ParseFloat(parts[0], 64)
    if err != nil {
        fmt.Println("解析数字错误:", err)
        return
    }
    var b float64
    if operation != "sqrt" { // 平方根运算只需要一个参数
        if len(parts) != 3 {
            fmt.Println("输入格式错误")
            return
        }
        b, err = strconv.ParseFloat(parts[2], 64)
        if err != nil {
            fmt.Println("解析数字错误:", err)
            return
        }
    }
    result, err := calculate(a, b, operation)
    if err != nil {
        fmt.Println("计算错误:", err)
        return
    }
    fmt.Printf("结果: %v\n", result)
}

扩展功能:支持更多运算

除了上述实现的运算外,我们可以根据需要添加更多的数学运算,比如三角函数计算、对数计算等。Go 语言的 math 包提供了丰富的数学函数,可以方便地实现这些功能。

通过这个案例的扩展,我们不仅学习了如何在 Go 语言中实现一个简单的命令行计算器,还探索了如何处理用户输入、执行条件判断以及使用标准库中的数学函数。这个小项目是理解函数调用、参数传递和错误处理等基本编程概念的一个很好的实践,同时也展示了命令行程序的基本结构和用户交互方式。随着你继续学习和实践,你将能够创建更复杂和功能丰富的应用程序。

目录
相关文章
|
6月前
|
存储 监控 算法
基于 Go 语言跳表结构的局域网控制桌面软件进程管理算法研究
针对企业局域网控制桌面软件对海量进程实时监控的需求,本文提出基于跳表的高效管理方案。通过多级索引实现O(log n)的查询、插入与删除性能,结合Go语言实现并发安全的跳表结构,显著提升进程状态处理效率,适用于千级进程的毫秒级响应场景。
271 15
|
6月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
351 4
|
7月前
|
存储 监控 算法
企业电脑监控系统中基于 Go 语言的跳表结构设备数据索引算法研究
本文介绍基于Go语言的跳表算法在企业电脑监控系统中的应用,通过多层索引结构将数据查询、插入、删除操作优化至O(log n),显著提升海量设备数据管理效率,解决传统链表查询延迟问题,实现高效设备状态定位与异常筛选。
193 3
|
7月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
221 4
|
7月前
|
存储 Java 编译器
对比Java学习Go——程序结构与变量
本节对比了Java与Go语言的基础结构,包括“Hello, World!”程序、代码组织方式、入口函数定义、基本数据类型及变量声明方式。Java强调严格的面向对象结构,所有代码需置于类中,入口方法需严格符合`public static void main(String[] args)`格式;而Go语言结构更简洁,使用包和函数组织代码,入口函数为`func main()`。两种语言在变量声明、常量定义、类型系统等方面也存在显著差异,体现了各自的设计哲学。
280 0
|
9月前
|
存储 安全 算法
Go语言泛型-泛型对代码结构的优化
Go语言自1.18版本引入泛型,极大提升了代码的通用性与可维护性。通过泛型,开发者可以减少重复代码、提高类型安全性,并增强程序的复用性和可读性。本文详细介绍了泛型在数据结构、算法及映射功能中的应用,展示了其在优化代码结构方面的优势。同时,Go编译器对泛型代码进行类型推导,确保运行时性能不受影响。合理使用泛型,有助于构建更加灵活高效的程序。
|
9月前
|
消息中间件 存储 算法
Go语言实战案例-自定义队列结构
本案例讲解如何使用 Go 语言通过结构体和切片实现自定义队列结构,涵盖入队、出队、查看队头元素及判空等基本操作,并提供完整示例代码与运行结果,适合初学者学习队列数据结构的实现与应用。
|
9月前
|
存储 算法 安全
Go语言实战案例-自定义栈结构
本案例详解如何用Go语言自定义栈结构,涵盖栈的压栈、弹栈、查看栈顶等基本操作,适合初学者掌握数据结构与算法基础。
|
10月前
|
人工智能 Dart Go
Go语言中的make和new函数的区别及使用场景
本文详细解析了Go语言中`make`和`new`函数的使用方法及区别。`make`用于创建切片、映射和通道等引用类型,返回初始化后的值;`new`用于创建任意类型的零值对象,返回指向该对象的指针。文章通过多个示例说明两者的应用场景,并总结了面试中可能遇到的相关问题,如底层实现、使用场景及优缺点等,帮助读者更好地理解和区分这两个函数。
333 1
|
11月前
|
Go 调度
GO语言函数的内部运行机制分析
以上就是Go语言中函数的内部运行机制的概述,展示了函数在Go语言编程中如何发挥作用,以及Go如何使用简洁高效的设计,使得代码更简单,更有逻辑性,更易于理解和维护。尽管这些内容深入了一些底层的概念,但我希望通过这种方式,将这些理论知识更生动、更形象地带给你,让你在理解的同时找到编程的乐趣。
210 5