50周学习go语言:第四周 函数与错误处理深度解析
第四周 函数与错误处理深度解析
以下是第4周函数基础的深度教程,包含两个完整案例和详细实现细节:
第四周:函数与错误处理深度解析
一、函数定义与参数传递
1. 基础函数结构
// 基本语法
func 函数名(参数列表) 返回值类型 {// 函数体
}// 示例:计算圆面积
func circleArea(radius float64) float64 {return math.Pi * radius * radius
}
2. 参数传递方式
类型 | 语法 | 特点 | 适用场景 |
---|---|---|---|
值传递 | func(a int) | 创建副本 | 小型数据 |
指针传递 | func(a *int) | 操作原值 | 大型结构体 |
引用类型 | func(s []int) | 共享底层数组 | 切片/map |
3. 参数类型详解
案例1:值传递 vs 指针传递
// 值传递示例
func addValue(n int) {n += 10
}// 指针传递示例
func addPointer(n *int) {*n += 10
}func main() {num := 5addValue(num) // 不影响原值addPointer(&num) // 修改原值fmt.Println(num) // 输出15
}
二、返回值与错误处理
1. 多返回值
// 返回商和余数
func div(a, b int) (int, int) {return a/b, a%b
}// 使用
q, r := div(17, 5) // q=3, r=2
2. 错误处理规范
// 标准错误返回格式
func sqrt(x float64) (float64, error) {if x < 0 {return 0, errors.New("负数不能求平方根")}return math.Sqrt(x), nil
}
3. 错误处理实践
案例2:安全除法函数
func safeDivide(a, b int) (int, error) {if b == 0 {return 0, fmt.Errorf("除数不能为零 (a=%d, b=%d)", a, b)}return a / b, nil
}// 使用示例
result, err := safeDivide(10, 0)
if err != nil {log.Printf("计算失败: %v", err)// 输出:计算失败: 除数不能为零 (a=10, b=0)
}
三、素数判断任务实现
需求分析
- 函数
isPrime
接收整数参数 - 返回:
true
:质数false
:非质数error
:输入非法(n < 2)
- 使用优化算法(试除法到平方根)
版本1:基础实现
func isPrime(n int) (bool, error) {if n < 2 {return false, fmt.Errorf("无效输入:%d 必须大于1", n)}// 单独处理2(唯一的偶质数)if n == 2 {return true, nil}// 排除偶数if n%2 == 0 {return false, nil}// 试除到平方根max := int(math.Sqrt(float64(n)))for i := 3; i <= max; i += 2 {if n%i == 0 {return false, nil}}return true, nil
}
版本2:性能优化(预计算小质数)
var smallPrimes = []int{2, 3, 5, 7, 11, 13}func optimizedIsPrime(n int) (bool, error) {if n < 2 {return false, fmt.Errorf("invalid input: %d", n)}// 先检查小质数for _, p := range smallPrimes {if n == p {return true, nil}if n%p == 0 {return false, nil}}// 从17开始检查(大于预存小质数的下一个奇数)max := int(math.Sqrt(float64(n)))for i := 17; i <= max; i += 2 {if n%i == 0 {return false, nil}}return true, nil
}
四、测试驱动开发
1. 表格驱动测试
func TestIsPrime(t *testing.T) {tests := []struct {name stringinput intwant boolwantErr bool}{{"负数测试", -5, false, true},{"0测试", 0, false, true},{"1测试", 1, false, true},{"最小质数", 2, true, false},{"偶合数", 4, false, false},{"大质数", 9973, true, false},{"平方数", 25, false, false},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {got, err := isPrime(tt.input)if (err != nil) != tt.wantErr {t.Errorf("isPrime() error = %v, wantErr %v", err, tt.wantErr)return}if got != tt.want {t.Errorf("isPrime() = %v, want %v", got, tt.want)}})}
}
2. 性能基准测试
func BenchmarkIsPrime(b *testing.B) {for i := 0; i < b.N; i++ {isPrime(104729) // 第10000个质数}
}
五、错误处理进阶
1. 错误包装
func process(n int) error {if valid, err := isPrime(n); err != nil {return fmt.Errorf("处理失败: %w", err) // 使用%w包装错误} else if !valid {return errors.New("需要质数输入")}return nil
}
2. 错误类型断言
func handleError(err error) {if pe, ok := err.(*PrimeError); ok {fmt.Printf("质数错误代码%d: %s\n", pe.Code, pe.Message)} else {fmt.Println("通用错误:", err)}
}// 自定义错误类型
type PrimeError struct {Code intMessage string
}func (e *PrimeError) Error() string {return fmt.Sprintf("错误%d: %s", e.Code, e.Message)
}
六、扩展练习
-
质因数分解函数:
func primeFactors(n int) ([]int, error) {// 返回所有质因数// 示例:输入12 → [2, 2, 3] }
-
并发素数筛选:
// 实现并发版本的埃拉托斯特尼筛法 func sieve(max int) []int {// 返回所有小于max的质数 }
七、学习检查清单
- 能正确声明带多个返回值的函数
- 会使用指针参数修改外部变量
- 理解error接口的使用场景
- 能编写表格驱动测试用例
- 会处理负数输入的边界情况
- 能解释试除法的优化原理
- 理解自定义错误类型的优势
通过这两个完整案例(安全除法与素数判断),你已掌握Go函数的定义、参数传递、错误处理和测试方法。建议:
- 实际运行测试用例观察结果
- 使用
go test -bench .
查看性能差异 - 尝试为优化版本添加更多预计算质数
- 实现扩展练习中的质因数分解功能
以下同扩展部分的代码与分析
以下是两个扩展练习的完整实现代码,包含详细注释说明每个关键步骤:
扩展练习1:质因数分解函数
// primeFactors 返回给定整数的质因数分解结果
// 输入:n - 需要分解的正整数(n >= 2)
// 返回:质因数切片(升序排列)和可能的错误
func primeFactors(n int) ([]int, error) {// 输入验证if n < 2 {return nil, fmt.Errorf("无效输入:%d 必须大于1", n)}factors := make([]int, 0)original := n// 处理偶数因子for n%2 == 0 {factors = append(factors, 2)n /= 2}// 从3开始检查奇数因子(步长2)for i := 3; i*i <= n; i += 2 {// 当i是因子时持续除尽for n%i == 0 {factors = append(factors, i)n /= i}}// 处理剩余的大质数因子if n > 2 {factors = append(factors, n)}// 验证分解结果product := 1for _, f := range factors {product *= f}if product != original {return nil, fmt.Errorf("分解验证失败:%d != %d", product, original)}return factors, nil
}/* 使用示例:
factors, err := primeFactors(360)
if err != nil {log.Fatal(err)
}
fmt.Println(factors) // 输出:[2 2 2 3 3 5]
*/
扩展练习2:并发素数筛选(埃拉托斯特尼筛法)
// sieve 使用并发筛法返回所有小于max的质数
func sieve(max int) []int {if max < 2 {return []int{}}// 创建初始数字生成器(2到max)ch := generateNumbers(2, max)primes := make([]int, 0)for {// 从当前通道获取候选质数p, ok := <-chif !ok {break}primes = append(primes, p)// 创建新的过滤通道ch = filter(ch, p)}return primes
}// generateNumbers 生成连续整数序列
func generateNumbers(start, end int) <-chan int {ch := make(chan int)go func() {for i := start; i <= end; i++ {ch <- i}close(ch)}()return ch
}// filter 创建新的通道过滤特定质数的倍数
func filter(in <-chan int, prime int) <-chan int {out := make(chan int)go func() {for n := range in {// 过滤掉能被当前质数整除的数if n%prime != 0 {out <- n}}close(out)}()return out
}/* 使用示例:
primes := sieve(30)
fmt.Println(primes) // 输出:[2 3 5 7 11 13 17 19 23 29]
*/
代码关键点解析
质因数分解函数
- 输入验证:确保输入符合质因数分解的基本要求
- 分阶段处理:
- 先处理所有偶数因子(2的幂次)
- 再处理奇数因子(从3开始,步长2)
- 验证机制:最终乘积验证确保分解的正确性
- 性能优化:
- 循环上限设置为√n(i*i <= n)
- 跳过偶数检查(通过步长2)
并发素数筛法
- 通道链式结构:
- 动态过滤:
- 每个新发现的质数都会创建一个新的过滤通道
- 前序通道处理完成后自动关闭
- 并发控制:
- 每个filter操作在独立的goroutine中执行
- 通过通道关闭机制自然终止循环
测试用例
质因数分解测试
func TestPrimeFactors(t *testing.T) {tests := []struct {input intexpect []int}{{12, []int{2, 2, 3}},{360, []int{2, 2, 2, 3, 3, 5}},{9973, []int{9973}}, // 大质数{1024, []int{2, 2, 2, 2, 2, 2, 2, 2, 2, 2}},}for _, tt := range tests {result, _ := primeFactors(tt.input)if !reflect.DeepEqual(result, tt.expect) {t.Errorf("输入%d 期望%v 实际%v", tt.input, tt.expect, result)}}
}
并发筛法测试
func TestSieve(t *testing.T) {expected := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}result := sieve(30)if !reflect.DeepEqual(result, expected) {t.Errorf("期望%v 实际%v", expected, result)}
}
性能对比
实现方式 | 时间复杂度 | 空间复杂度 | 适合场景 |
---|---|---|---|
传统筛法 | O(n log log n) | O(n) | 大数据量 |
并发筛法 | O(n)(理论) | O(k)(k为goroutine数量) | 学习并发原理 |
实际测试显示,当max超过1,000,000时,传统实现效率更高。并发版本更适合理解通道和goroutine的协作机制。
扩展方向
-
质因数分解优化:
- 预生成小质数列表加速试除过程
- 添加Pollard’s Rho算法处理大数分解
-
并发筛法改进:
- 限制最大goroutine数量
- 添加超时控制
- 实现分片并行处理
这两个练习展示了Go语言在处理数学问题和并发编程方面的优势,建议通过实际调试观察通道数据的流动过程,可以更深入理解并发筛法的工作原理。
相关文章:
50周学习go语言:第四周 函数与错误处理深度解析
第四周 函数与错误处理深度解析 以下是第4周函数基础的深度教程,包含两个完整案例和详细实现细节: 第四周:函数与错误处理深度解析 一、函数定义与参数传递 1. 基础函数结构 // 基本语法 func 函数名(参数列表) 返回值类型 {// 函数体 }// …...
debian 12安装 postgresql 17
按照官方文档安装,即可安装成功 https://www.postgresql.org/download/linux/debian/ 添加存储库 #添加存储库 sudo apt install -y postgresql-common#执行 存储库内 命令,自动处理某些东西 sudo /usr/share/postgresql-common/pgdg/apt.postgresql.o…...
C++....................4
1. using namespace std; class mystring { private:char* p;int len;// 辅助函数:复制字符串void copy(const char* source) {len strlen(source);p new char[len 1];strcpy(p, source);}// 辅助函数:释放内存void release() {if (…...
图书馆系统源码详解
本项目是一个基于Scala语言开发的图书馆管理系统。系统主要由以下几个部分组成:数据访问层(DAO)、数据模型层(Models)、服务层(Service)以及用户界面层(UI)。以下是对项目…...
Node.js中如何修改全局变量的几种方式
Node.js中如何修改全局变量。我需要先理解他们的需求。可能他们是在开发过程中遇到了需要跨模块共享数据的情况,或者想要配置一些全局可访问的设置。不过,使用全局变量可能存在一些问题,比如命名冲突、难以维护和测试困难,所以我得…...

基于javaweb的SpringBoot个人博客系统设计和实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...

厦大团队:DeepSeek大模型概念、技术与应用实践 140页PDF完整版下载
DeepSeek使用教程系列: 厦门大学: DeepSeek大模型概念、技术与应用实践 140页PDF完整版文件 厦大团队:DeepSeek大模型概念、技术与应用实践(140页PPT读懂大模型).pdf https://pan.baidu.com/s/1de4UIxqPsvMBIYcpen_M-…...

【Blender】二、建模篇--05,阵列修改器与晶格形变
阵列修改器是bender里面一个比较常用的修改器,所以我们单独开口来讲,我们会先从几片树叶出发,然后我们用阵列修改器把这几片树叶变成这样的造型和这样的造型。这两个造型分别就代表着阵列修改器最常用的两种偏移方法,我们现在就开始我们先来做几个树叶。 1.树叶建模 首先…...

#渗透测试#批量漏洞挖掘#畅捷通T+远程命令执行漏洞
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 一、漏洞概况 二、攻击特征 三、应急处置…...
【Python爬虫(23)】探秘Python爬虫数据存储:MongoDB实战指南
【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取ÿ…...
Pytorch使用手册-音频数据增强(专题二十)
音频数据增强 torchaudio 提供了多种方式来增强音频数据。 在本教程中,我们将介绍一种应用效果、滤波器、RIR(房间脉冲响应)和编解码器的方法。 最后,我们将从干净的语音合成带噪声的电话语音。 import torch import torchaudio import torchaudio.functional as Fprin…...
Linux 命令大全完整版(04)
1. 用户信息相关命令 who 功能说明:显示目前登入系统的用户信息。语 法:who [-Himqsw][--help][--version][am i][记录文件]补充说明:执行这项指令可得知目前有哪些用户登入系统,单独执行 who 指令会列出登入帐号、使用的终端…...
嵌入式Linux内核底层调试技术Kprobes
大家好,我是bug菌~ Kprobes 是 Linux 内核中一种动态插桩(Dynamic Instrumentation)技术,允许在不修改内核源码或重启系统的前提下,动态监控内核函数的执行。它是内核调试、性能分析和安全监控的重要工具。以下从技术…...

leetcode 119. 杨辉三角 II
给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出: [1,1]提示…...

内网网络安全的解决之道
本文简要分析了企业内部网络所面临的主要分析,阐述了安全管理人员针对不同威胁的主要技术应对措施。进一步介绍了业界各种技术措施的现状,并提出了未来可能的发展趋势。 内网网络安全问题的提出 网络安全对于绝大多数人而言指的都是互联网安全ÿ…...
分布式光纤声波振动技术在钻井泄漏检测中的应用
在石油天然气的钻井作业中,及时发现并定位泄漏点对于保障开采安全、降低环境污染以及避免经济损失至关重要。传统的泄漏检测方法往往存在局限性,而分布式光纤声波振动技术凭借其独特的优势,正逐渐成为钻井过程中寻找泄漏的有力工具。 技术原理…...

deepseek 导出导入模型(Windows)
前言 实现导出导入deepseek 模型。deepseek 安装Windows下参考 Windows 导出模型 Restart-Service 重启服务参考Stop-Service 关闭服务参考Start-Service 确定服务参考Compress-Archive 压缩参考Expand-Archive 解压参考setx 环境变量参考C:\Users\用户名\.ollama\models 默…...
Spring MVC配置文件
1. DispatcherServlet配置 作用:DispatcherServlet是Spring MVC的核心前端控制器,用于接收所有HTTP请求,并将请求分发给对应的处理器(Controller)。 配置方式: 在web.xml中配置DispatcherServletÿ…...

计算机视觉:主流数据集整理
第一章:计算机视觉中图像的基础认知 第二章:计算机视觉:卷积神经网络(CNN)基本概念(一) 第三章:计算机视觉:卷积神经网络(CNN)基本概念(二) 第四章:搭建一个经典的LeNet5神经网络(附代码) 第五章࿱…...

基于AT89C52单片机的出租车计价器
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/90419909?spm1001.2014.3001.5501 C17 部分参考设计如下: 摘要 随着城市交通行业的迅速发展,出租车作为最主要的城市公共交通工具之一…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...

Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...

【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...