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

news2024/11/25 0:36:22

在这里插入图片描述

文章目录

  • 2.1 条件语句:决策的艺术
    • 2.1.1 基础知识讲解
    • 2.1.2 重点案例:用户角色权限判断
          • 实现用户角色权限判断
          • 扩展功能
          • 实现代码
          • 功能扩展:添加或删除用户
    • 2.1.3 拓展案例 1:成绩等级判断
          • 实现成绩等级判断功能
          • 实现代码
          • 扩展功能:详细反馈
    • 2.1.4 拓展案例 2:简单的命令行解析
          • 功能描述
          • 实现代码
          • 扩展功能:命令行帮助信息
  • 2.2 循环控制:编织代码的循环魔法
    • 2.2.1 基础知识讲解
    • 2.2.2 重点案例:文件逐行读取并处理
      • 功能描述
      • 实现代码
      • 扩展功能
    • 2.2.3 拓展案例 1:生成斐波那契数列
      • 功能描述
      • 实现代码
          • 性能比较
    • 2.2.4 拓展案例 2:批量处理用户数据
      • 功能描述
      • 实现代码
      • 扩展功能:用户分组
  • 2.3 函数定义与使用:Go 语言的超能力
    • 2.3.1 基础知识讲解
    • 2.3.2 重点案例:数据过滤器
      • 功能描述
      • 实现代码
      • 扩展功能:自定义过滤条件
    • 2.3.3 拓展案例 1:字符串翻转
      • 功能描述
      • 实现代码
      • 扩展功能:处理多语言文本
      • 更多字符串操作
    • 2.3.4 拓展案例 2:简单的计算器
      • 功能描述
      • 实现代码
      • 扩展功能:支持更多运算

2.1 条件语句:决策的艺术

在 Go 语言的世界中,条件语句是你的决策指南,它引导你的程序在不同情况下选择正确的路径。就像在生活中一样,面对选择,我们需要智慧来做出最佳决策。

2.1.1 基础知识讲解

if 语句

if 语句是最基本的条件判断结构。它让程序能够执行基于布尔表达式结果的条件代码块。

if condition {
    // 条件为 true 时执行
} else {
    // 条件为 false 时执行
}

if-else if 结构

当你有多个条件需要判断时,if-else if 结构非常有用。

if condition1 {
    // 条件1为 true 时执行
} else if condition2 {
    // 条件2为 true 时执行
} else {
    // 其他情况执行
}

switch 语句

switch 语句提供了一种简洁的方式来执行多路分支选择。与多个 if-else 相比,switch 在处理多个条件判断时更加清晰和易于管理。

switch variable {
case value1:
    // 变量等于 value1 时执行
case value2:
    // 变量等于 value2 时执行
default:
    // 默认情况执行
}

2.1.2 重点案例:用户角色权限判断

在构建一个应用时,经常需要根据用户的角色来判断他们可以访问哪些资源或执行哪些操作。这个过程被称为权限控制。通过一个更实际的示例,我们将展示如何使用 Go 语言来实现一个简单的用户角色和权限判断系统。这个系统将演示如何处理不同用户角色的权限,并给出相应的操作提示。

实现用户角色权限判断

假设我们的应用有三种用户角色:管理员(admin)、编辑(editor)和访客(visitor)。每种角色都有不同的权限级别。我们的目标是根据用户的角色,输出他们可以执行的操作。

扩展功能

除了基本的角色判断,我们还将添加一个功能,允许管理员添加或删除用户角色,以展示如何在实际应用中动态处理权限。

实现代码
package main

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

func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Println("请输入你的角色(admin, editor, visitor):")
    roleInput, _ := reader.ReadString('\n')
    role := strings.TrimSpace(roleInput)

    fmt.Println("可执行的操作:")
    switch role {
    case "admin":
        fmt.Println("1. 发布新内容")
        fmt.Println("2. 编辑内容")
        fmt.Println("3. 删除内容")
        fmt.Println("4. 添加或删除用户")
    case "editor":
        fmt.Println("1. 发布新内容")
        fmt.Println("2. 编辑内容")
        // 编辑没有删除内容的权限
    case "visitor":
        fmt.Println("1. 查看内容")
        // 访客只有查看内容的权限
    default:
        fmt.Println("未知角色,没有可执行的操作。")
    }
}
功能扩展:添加或删除用户

为了演示动态处理权限,我们可以模拟管理员添加或删除用户角色的过程。在简化的示例中,我们仅通过命令行输入来模拟这一过程。

if role == "admin" {
    fmt.Println("是否需要添加或删除用户?(yes/no)")
    decisionInput, _ := reader.ReadString('\n')
    decision := strings.TrimSpace(decisionInput)

    if decision == "yes" {
        fmt.Println("执行添加或删除用户操作...")
        // 这里可以添加实际的添加或删除用户的逻辑
    }
}

通过这个扩展案例,我们不仅掌握了如何使用条件语句来处理用户角色和权限,还学习了如何根据实际需求灵活扩展程序的功能。这种权限控制模式在构建需要用户认证和授权的应用时非常常见,是每位 Go 程序员必须掌握的技能之一。通过实践这些案例,你将能够更好地理解和应用 Go 语言的条件语句,为构建更复杂的应用打下坚实的基础。

2.1.3 拓展案例 1:成绩等级判断

在教育软件或学生管理系统中,成绩等级判断是一个基础但极其重要的功能。它帮助教师、学生及家长快速了解学生的学业表现。通过这个扩展案例,我们将创建一个简单的命令行程序,用于输入学生的成绩,然后输出相应的成绩等级。

实现成绩等级判断功能

我们的成绩等级判断功能将基于以下标准:

  • 90 分及以上为 A 等级
  • 80 到 89 分为 B 等级
  • 70 到 79 分为 C 等级
  • 60 到 69 分为 D 等级
  • 60 分以下为 F 等级
实现代码
package main

import (
    "fmt"
)

// grade 判断并返回成绩等级
func grade(score int) string {
    switch {
    case score >= 90:
        return "A"
    case score >= 80:
        return "B"
    case score >= 70:
        return "C"
    case score >= 60:
        return "D"
    default:
        return "F"
    }
}

func main() {
    var score int
    fmt.Print("请输入学生的成绩:")
    fmt.Scanf("%d", &score)

    gradeLevel := grade(score)
    fmt.Printf("成绩等级为:%s\n", gradeLevel)
}
扩展功能:详细反馈

除了基本的成绩等级判断,我们可以为每个等级提供一些具体的学习建议,给予学生更加个性化和具体的反馈。

// detailedFeedback 根据成绩等级提供详细反馈
func detailedFeedback(grade string) {
    switch grade {
    case "A":
        fmt.Println("非常优秀!继续保持。")
    case "B":
        fmt.Println("做得很好,但还有提升的空间。")
    case "C":
        fmt.Println("完成了基本要求,努力提高吧。")
    case "D":
        fmt.Println("需要更加努力,你可以做得更好。")
    case "F":
        fmt.Println("请认真反思并寻求帮助,不要气馁!")
    }
}

func main() {
    var score int
    fmt.Print("请输入学生的成绩:")
    fmt.Scanf("%d", &score)

    gradeLevel := grade(score)
    fmt.Printf("成绩等级为:%s\n", gradeLevel)
    detailedFeedback(gradeLevel)
}

通过这个案例,我们不仅学会了如何使用 switch 语句来处理多个条件判断,还展示了如何在实际应用中为用户提供有价值的反馈。这种方法可以应用于各种情境,比如游戏成就系统、客户服务评级等,为用户提供更加丰富和个性化的体验。

2.1.4 拓展案例 2:简单的命令行解析

命令行工具是开发人员经常需要创建和使用的工具,它们允许用户通过终端或命令提示符与程序交互。在这个扩展案例中,我们将构建一个简单的命令行程序,该程序可以解析用户输入的命令,并执行相应的操作。这个案例将展示如何在 Go 语言中处理命令行参数,提供一个实用的命令行解析示例。

功能描述

假设我们需要开发一个工具,支持以下命令:

  • start:启动程序
  • stop:停止程序
  • status:显示程序状态

根据用户输入的命令,程序将执行相应的操作并给出反馈。

实现代码
package main

import (
    "fmt"
    "os"
)

func main() {
    if len(os.Args) < 2 {
        fmt.Println("错误:缺少命令参数。")
        fmt.Println("使用方法:mytool <command>")
        fmt.Println("命令:")
        fmt.Println("  start  - 启动程序")
        fmt.Println("  stop   - 停止程序")
        fmt.Println("  status - 显示程序状态")
        return
    }

    command := os.Args[1]

    switch command {
    case "start":
        fmt.Println("程序已启动...")
        // 这里可以放置实际启动程序的代码
    case "stop":
        fmt.Println("程序已停止...")
        // 这里可以放置实际停止程序的代码
    case "status":
        fmt.Println("程序正在运行中...")
        // 这里可以放置获取程序状态的代码
    default:
        fmt.Println("未知命令:", command)
        fmt.Println("可用命令:start, stop, status")
    }
}
扩展功能:命令行帮助信息

为了提高用户体验,我们可以为我们的命令行工具添加一个帮助命令,当用户输入 help 时,显示所有可用的命令及其描述。

func printHelp() {
    fmt.Println("使用方法:mytool <command>")
    fmt.Println("命令:")
    fmt.Println("  start  - 启动程序")
    fmt.Println("  stop   - 停止程序")
    fmt.Println("  status - 显示程序状态")
    fmt.Println("  help   - 显示帮助信息")
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("错误:缺少命令参数。")
        printHelp()
        return
    }

    command := os.Args[1]

    switch command {
    case "start", "stop", "status":
        fmt.Printf("执行 '%s' 命令...\n", command)
        // 实际的启动、停止、状态检查逻辑可以在这里实现
    case "help":
        printHelp()
    default:
        fmt.Println("未知命令:", command)
        printHelp()
    }
}

通过这个案例,我们学习了如何在 Go 程序中解析和处理命令行参数,实现了一个简单但功能齐全的命令行工具。这种技能在开发实用程序、脚本或其他需要与用户交互的应用时非常有用。随着你对 Go 语言的进一步学习,你将能够创建更复杂和强大的命令行应用,满足更广泛的需求。

2.2 循环控制:编织代码的循环魔法

在 Go 的神奇世界里,循环控制是我们编织代码故事的基本魔法之一。就像在生活中重复日常习惯一样,循环允许我们在代码中执行重复的操作,直到满足某个条件。让我们一起探索这项魔法,看看如何在实际项目中巧妙地使用它。

2.2.1 基础知识讲解

for 循环

Go 语言中的 for 循环是唯一的循环语句,但它非常灵活,足以表达所有的循环模式。

  • 基本 for 循环:

    for 初始化语句; 条件表达式; 后置语句 {
        // 循环体
    }
    
  • 省略初始化和后置语句的 for 循环(类似其他语言的 while 循环):

    for 条件表达式 {
        // 循环体
    }
    
  • 无限循环:

    for {
        // 循环体
    }
    

range 循环

range 循环用于遍历数组、切片、字符串、map 或通道(channel)。range 会返回索引(或键)和值。

for 索引,:= range 集合 {
    // 循环体
}

2.2.2 重点案例:文件逐行读取并处理

在现实世界的编程任务中,我们经常需要从文件中读取数据,然后对这些数据进行处理。这个过程对于数据分析、日志处理等应用尤其重要。通过扩展我们的案例,我们将创建一个更实用的程序,该程序不仅读取文件中的每一行数据,还会进行一些基本的数据处理操作,比如统计文件中单词的数量、最长的行等。

功能描述

  1. 逐行读取文件内容。
  2. 统计文件总行数。
  3. 统计文件中单词的总数。
  4. 找出文件中最长的一行。

实现代码

假设我们的文件包含英文文本,我们将通过以下程序实现上述功能:

package main

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

func main() {
    filePath := "example.txt" // 假设这是我们要读取的文件
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Println("打开文件时出错:", err)
        return
    }
    defer file.Close()

    var lineCount, wordCount, maxLineLength int
    var longestLine string
    scanner := bufio.NewScanner(file)

    for scanner.Scan() {
        line := scanner.Text()
        lineCount++
        words := strings.Fields(line) // 使用空格分隔单词
        wordCount += len(words)

        if len(line) > maxLineLength {
            maxLineLength = len(line)
            longestLine = line
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Println("读取文件时出错:", err)
        return
    }

    fmt.Printf("文件 '%s' 总行数: %d\n", filePath, lineCount)
    fmt.Printf("文件 '%s' 总单词数: %d\n", filePath, wordCount)
    fmt.Printf("文件 '%s' 中最长的行: %s\n", filePath, longestLine)
}

扩展功能

为了使程序更加实用,我们可以添加一些附加功能,比如:

  • 写入统计结果到一个新文件。
  • 提供命令行参数来指定输入文件和输出文件的路径。

通过这个案例扩展,我们展示了如何使用 Go 语言进行文件的逐行读取和基本的文本处理。这个程序是许多数据处理任务的基础,比如日志分析、数据清洗等。通过学习和实践这样的案例,你将能够更好地理解文件操作和字符串处理在 Go 中的应用,为处理更复杂的数据处理任务打下坚实的基础。

2.2.3 拓展案例 1:生成斐波那契数列

斐波那契数列是通过如下简单的递归关系定义的:每个数是前两个数之和,序列以 0 和 1 开始。这个数列在数学、计算机科学甚至是自然界的设计中都有广泛的应用。通过这个案例,我们将扩展我们的程序来生成斐波那契数列,并提供几种不同的实现方式,展示它们在实际编程中的应用。

功能描述

  1. 生成斐波那契数列的前 N 个数字。
  2. 提供迭代和递归两种实现方式。
  3. 比较两种实现方式的性能差异。

实现代码

迭代实现

迭代实现是生成斐波那契数列的最直接和高效的方式。以下是迭代方法的 Go 程序实现:

package main

import (
    "fmt"
    "time"
)

// fibonacciIterative 通过迭代方式生成斐波那契数列
func fibonacciIterative(n int) []int {
    if n <= 0 {
        return []int{}
    }
    fibs := make([]int, n)
    fibs[0] = 0
    if n > 1 {
        fibs[1] = 1
        for i := 2; i < n; i++ {
            fibs[i] = fibs[i-1] + fibs[i-2]
        }
    }
    return fibs
}

func main() {
    var n int
    fmt.Print("请输入斐波那契数列的长度: ")
    fmt.Scanf("%d", &n)

    start := time.Now()
    fibs := fibonacciIterative(n)
    elapsed := time.Since(start)

    fmt.Printf("斐波那契数列的前 %d 个数字为: %v\n", n, fibs)
    fmt.Printf("迭代实现耗时: %s\n", elapsed)
}

递归实现

虽然递归实现在理解上更直接,但它在计算较大的斐波那契数时会变得非常慢,因为它包含大量的重复计算。

// fibonacciRecursive 通过递归方式生成斐波那契数列的第 N 个数字
func fibonacciRecursive(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacciRecursive(n-1) + fibonacciRecursive(n-2)
}

func main() {
    var n int
    fmt.Print("请输入要计算的斐波那契数列长度: ")
    fmt.Scanf("%d", &n)

    start := time.Now()
    for i := 0; i < n; i++ {
        fmt.Print(fibonacciRecursive(i), " ")
    }
    elapsed := time.Since(start)

    fmt.Printf("\n递归实现耗时: %s\n", elapsed)
}
性能比较

通过实际运行这两段程序,你会发现迭代实现在处理大量数据时明显快于递归实现。递归实现在 n 较小的时候非常高效,但随着 n 的增加,计算时间急剧增加,这是由于递归中存在大量的重复计算。

通过对比这两种实现方式,我们不仅学习了斐波那契数列的生成,还理解了迭代与递归在实际应用中的性能差异,为我们在面对不同编程问题时选择最合适的解决方案提供了宝贵的经验。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1451740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring中的事务实现、失效场景即AOP的相关概念理解

spring实现事务&#xff08;声明式事务&#xff09;的本质就是aop完成的,它会对方法的前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或回滚事务。aop就是面向切面编程&#xff0c;在spring中将那些与业务无关&#xff0c;但却对多个对象产生影响的…

Opencv简单图像操作

Opencv 一、读取图片 1.imshow Mat imread(const string& filename, intflags1 );flags: enum { /* 8bit, color or not */CV_LOAD_IMAGE_UNCHANGED -1, /* 8bit, gray */CV_LOAD_IMAGE_GRAYSCALE 0, /* ?, color */CV_LOAD_IMAGE_COLOR 1, /* any depth, ? */…

深度学习之线性模型

深度学习之线性模型 y w * x模型思路 y w * x b模型思路 y w * x模型 思路 这里求权重w , 求最适合的权重&#xff0c;就是求损失值最小的时候 这里用穷举法:在一个范围内&#xff0c;列出w的所有值&#xff0c;并且计算出每组数据的平均损失值,以w 为横坐标, 损失值为纵坐…

定时器按秒计时

一、函数学习 二、代码、 main.c #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Timer.h"uint16_t Num;int main(void) {OLED_Init();Timer_Init(); OLED_ShowString(1,1,"Num:…

树莓派5 EEPROM引导加载程序恢复镜像

树莓派5不能正常启动&#xff0c;可以通过电源led灯的闪码来判断错误发生的大致情形。 LED警告闪码 如果树莓派由于某种原因无法启动&#xff0c;或者不得不关闭&#xff0c;在许多情况下&#xff0c;LED会闪烁特定的次数来指示发生了什么。LED会闪烁几次长闪烁&#xff0c;然…

Linux POSIX信号量 线程池

Linux POSIX信号量 线程池 一. 什么是POSIX信号量&#xff1f;二. POSIX信号量实现原理三. POSIX信号量接口函数四. 基于环形队列的生产消费模型五. 线程池 一. 什么是POSIX信号量&#xff1f; POSIX信号量是一种用于同步和互斥操作的机制&#xff0c;属于POSIX&#xff08;Po…

C++入门篇——命名空间

在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存 在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或名字污染&#xff0c;namespace关键…

Calendar的使用(Java)

直接从需求来理解&#xff1a;将2024年2月16日增加一个月 如果不使用Calendar的话&#xff0c;我们需要定义字符串记住这个日期&#xff0c;然后把字符串解析成Date日期对象&#xff0c;通过Date日期对象获取其毫秒值&#xff0c;然后增加一个月的毫秒值&#xff0c;再格式化时…

社区养老|社区养老服务系统|基于springboot社区养老服务系统设计与实现(源码+数据库+文档)

社区养老服务系统目录 目录 基于springboot社区养老服务系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员部分功能 &#xff08;1&#xff09; 用户管理 &#xff08;2&#xff09;服务种类管理 &#xff08;3&#xff09;社区服务管理 &#xff08…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第五天-ARM Linux编程之字符设备驱动(物联技术666)

链接&#xff1a;https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd1688 提取码&#xff1a;1688 教学内容&#xff1a; 1、内核模块的简单框架&#xff1a; __init __exit执行完后就释放空间 简单框架&#xff1a;包含三个部分 1&#xff09;模块初始化和模块退出函数…

题目:3.神奇的数组(蓝桥OJ 3000)

问题描述&#xff1a; 解题思路&#xff1a; 官方&#xff1a; 我的总结&#xff1a; 利用双指针遍历每个区间并判断是否符合条件&#xff1a;若一个区间符合条件则该区间在其左端点不变的情况下的每一个子区间都符合条件&#xff0c;相反若一个区间内左端点固定情况下有一个以…

排序算法---桶排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 桶排序&#xff08;Bucket Sort&#xff09;是一种排序算法&#xff0c;它将待排序的数据分到几个有序的桶中&#xff0c;每个桶再分别进行排序&#xff0c;最后将各个桶中的数据按照顺序依次取出&#xff0c;即可得到有序序…

MySQL免安装版安装教程

官网下载安装包 MySQL :: Download MySQL Community Server (Archived Versions) 选择mysql版本下载 安装配置MySQL 将下载完的Mysql安装包解压到指定目录 打开windos系统的cmd&#xff0c;以管理员身份运行 进入mysql文件夹中的bin目录 安装MySQL的服务mysqld --install 初…

【教程】C++语言基础学习笔记(五)——Vector向量

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【C语言基础学习】系列文章 第一章 《项目与程序结构》 第二章 《数据类型》 第三章 《运算符》 第四章 《流程控制》 第五章…

姿态传感器MPU6050模块之陀螺仪、加速度计、磁力计

MEMS技术 微机电系统&#xff08;MEMS, Micro-Electro-Mechanical System&#xff09;&#xff0c;也叫做微电子机械系统、微系统、微机械等&#xff0c;指尺寸在几毫米乃至更小的高科技装置。微机电系统其内部结构一般在微米甚至纳米量级&#xff0c;是一个独立的智能系统。 微…

C语言指针(初阶)

文章目录 1:内存与地址1.1内存1.2:如何理解编址 2:指针变量与地址2.1:指针变量与解引用操作符2.1.1:指针变量2.1.2:如何拆解指针类型2.1.3:解引用操作符 2.2:指针变量的大小 3:指针变量类型的意义代码1解引用修改前解引用修改后 代码2解引用修改前解引用修改后 4:const修饰指针…

【C语言期末项目-通讯录】-终级版本-可动态申请内存、可存储数据到文件(手把手详细过程,期末评分A+的项目,答辩辅助神博文,建议三连点赞收藏)

目录 ​编辑 前言&#xff1a; 1.项目功能需求分析 2.文件框架说明 3.程序主框架实现 4.创建联系人结构体类型和通讯录结构体类型 4.1创建通讯录 5.程序功能实现--封装功能函数实现不同功能 5.1通讯录初始化 5.2增加联系人 5.3显示所有联系人的信息 5.4删除指定…

BBC英式口语~发音练习~笔记整理

参考资料 原视频地址&#xff1a; https://www.bilibili.com/video/BV1D7411n7bS/?spm_id_from333.1245.0.0&vd_source5986fc7c8e6d754f3ca44233573aeaff 笔记图片

root MUSIC 算法补充说明

root MUSIC 算法补充说明 多项式求根root MUSIC 算法原理如何从 2 M − 2 2M-2 2M−2 个根中确定 K K K 个根从复数域上观察 2 M − 2 2M-2 2M−2 个根的分布 这篇笔记是上一篇关于 root MUSIC 笔记的补充。 多项式求根 要理解 root MUSIC 算法&#xff0c;需要理解多项式求…

DSA 经典数据结构与算法 学习心得和知识总结(三) |有向无环图及其应用

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《算法导论》第三版 就是这本被封神的杰作&#xff0c;就是它&#x1f926; 2、参考书籍&#xff1a;《数据结构》严奶奶版 3、参考书…