Go 语言的函数详解:语法、用法与最佳实践

news2024/11/26 21:21:14

在 Go 语言的世界里,函数是构建和维护任何应用程序的基石。不仅因为它们提供了一种将大问题划分为更小、更易管理部分的方法,而且还因为它们在 Go 程序中扮演着至关重要的角色。从简单的工具函数到复杂的系统级调用,理解和利用 Go 的函数特性是每个开发者必须掌握的技能。本文将带你深入浅出地探索 Go 函数的语法、特性以及它们如何被用来创建更加健壯和高效的应用程序。无论你是 Go 语言的新手还是有一定经验的开发者,这篇文章都会为你揭开 Go 函数的多个层面,帮助你更好地在实际项目中应用它们


文章目录

      • 1、Go 语言中的函数
        • 1.1、函数介绍
        • 1.2、函数定义
        • 1.3、函数分类
          • 1.3.1、普通函数
          • 1.3.2、匿名函数或 Lambda 函数
          • 1.3.3、类方法(Methods)
      • 2、方法和函数的区别
        • 2.1、接收器(Receiver)
        • 2.2、调用方式
        • 2.3、设计意图
        • 2.4、作用域和访问控制
      • 3、函数参数与返回值
        • 3.1、多值返回
        • 3.2、返回和错误处理
        • 3.3、按值传递 vs 按引用传递
        • 3.4、命名的返回值
        • 3.5、空白符 (blank identifier)
        • 3.6、改变外部变量
      • 4、函数调用
        • 4.1、调用同一个包定义的函数
        • 4.2、调用其他包定义的函数
        • 4.3、系统内置函数


1、Go 语言中的函数

1.1、函数介绍

在 Go 语言中,函数是基本的代码块,每一个程序都包含很多的函数。Go 函数的功能非常强大,以至于被认为拥有函数式编程语言的多种特性。

Go 是编译型语言,所以函数编写的顺序是无关紧要的;鉴于可读性的需求,最好把 main() 函数写在文件的前面,其他函数按照一定逻辑顺序进行编写(例如函数被调用的顺序)。

编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务(那就是函数)来解决。而且,同一个任务(函数)可以被调用多次,有助于代码重用。

(事实上,好的程序是非常注意 DRY 原则的,即不要重复你自己 (Don’t Repeat Yourself),意思是执行特定任务的代码只能在程序里面出现一次。)

当函数执行到代码块最后一行(} 之前)或者 return 语句的时候会退出,其中 return 语句可以带有零个或多个参数;这些参数将作为返回值供调用者使用。简单的 return 语句也可以用来结束 for 死循环,或者结束一个协程 (Goroutine)。

1.2、函数定义

Go语言函数的基本组成包括:关键字 func、函数名、参数列表、返回值、函数体和返回语句。Go 语言是强类型语言,无论是参数还是返回值,在定义函数时,都需要声明其类型。

img

如下是 Go 语言中函数的一个简单示例:

// 参数类型 int
// 返回类型 int
func add(a, b int) int  {
    return a + b
}

在 Go 语言中,确实有三种主要的函数类型,每种都有其独特的应用场景和重要性。下面是对这三种函数类型的简要解释和示例:

1.3、函数分类
1.3.1、普通函数

这是最常见的函数类型,用于定义可重复使用的代码块。这些函数可以有输入参数和返回值。函数的命名应该反映其功能,以便于理解和维护。

func add(a int, b int) int {
    return a + b
}
1.3.2、匿名函数或 Lambda 函数

匿名函数,即没有名称的函数,通常用于实现局部的、一次性的功能,不需要在其他地方调用。这些函数可以定义在变量中或直接在其它函数调用中作为参数传递。匿名函数能够捕获定义它们的作用域中的变量(闭包)。

func main() {
    sum := func(a int, b int) int {
        return a + b
    }
    println(sum(3, 4))  // 输出 7
}
1.3.3、类方法(Methods)

方法是附加到类型(通常是结构体或接口)的特殊类型的函数。在方法中,该类型的实例通常表示为接收者 receiver。这使得 Go 能够支持面向对象风格的编程。

type Rectangle struct {
    width, height float64
}

func (r Rectangle) Area() float64 {
    return r.width * r.height
}

在这个例子中,Area 是一个方法,它与 Rectangle 类型的实例关联,可以通过该类型的对象来调用,例如 r.Area()

每种函数类型在 Go 程序设计中都扮演着重要角色。普通函数强调逻辑的重用和模块化;匿名函数和闭包则便于编写快速的内联处理逻辑;方法则提供了一种将行为与数据结构绑定的方式,使得代码更加直观和面向对象。


2、方法和函数的区别

在 Go 语言中,方法和普通函数是两个基本的代码结构,它们都用于执行特定的任务,但在用途和定义方式上存在一些关键的区别。理解这些区别有助于更有效地使用 Go 语言进行面向对象风格的编程。以下是方法和普通函数之间的主要区别:

2.1、接收器(Receiver)

方法:

  • 方法在定义时会带有一个接收器,这个接收器指明了该方法附属于哪个类型。因此,一个方法是与类型(通常是结构体或者接口)绑定的。
  • 例如,func (r Rectangle) Area() float64 {...}Rectangle 类型的一个方法,其中 r 是类型为 Rectangle 的接收器,它使得 Area 方法能够访问 Rectangle 的实例字段。

函数:

  • 普通函数不包含接收器。它是独立存在的,不直接关联到任何数据结构。
  • 例如,func PrintArea(r Rectangle) {...} 是一个普通的函数,它需要显式接受一个 Rectangle 类型的参数来执行操作。
2.2、调用方式

方法:

  • 方法的调用是面向对象的,需要通过具体的类型实例来调用。即你需要一个类型的对象来调用它的方法。
  • 使用点符号(.)调用,如 rectangle.Area()

函数:

  • 函数的调用是独立的,只需要直接使用函数名并传递必要的参数。
  • PrintArea(rectangle)
2.3、设计意图

方法:

  • 方法通常用于实现与特定类型紧密相关的行为。它们是该类型的固有行为的一部分,强调了数据(对象状态)和行为(方法)的封装。
  • 方法是实现接口的关键,允许 Go 语言支持多态和接口的抽象特性。

函数:

  • 函数更倾向于执行通用的操作,不特定于某个数据类型。函数可以应用于多种类型的数据,或执行不依赖于状态的操作。
  • 函数适合执行不需要访问对象状态的工具性任务。
2.4、作用域和访问控制

方法:

  • 可以访问其接收器类型的所有字段,包括私有字段。这使得方法可以对数据进行更精细的操作。

函数:

  • 作为外部调用者,函数只能访问传递给它的数据的公共字段和方法,除非它定义在同一包内,这样也可以访问私有字段。

3、函数参数与返回值

3.1、多值返回

Go 允许函数返回多个值,这是其与许多其他语言的显著区别。这种特性特别有用,例如在错误处理中,一个函数可以同时返回操作结果和可能发生的错误。

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}
3.2、返回和错误处理

在 Go 中,任何具有返回值的函数都必须以 return 或在极端情况下使用 panic 结束。这样的设计强制开发者处理所有可能的执行路径,确保代码的完整性和稳定性。

在 Go 语言中,函数是基本的构建块,具有多样的参数和返回值处理方式,以及对变量作用域的灵活控制。这些特性让 Go 在函数设计上有别于其他一些传统的编程语言如 C、C++、Java 和 C#。下面,我们将详细梳理并拓展你提供的内容:

3.3、按值传递 vs 按引用传递

Go 默认使用按值传递,这意味着传递的是参数的副本。对副本的任何修改都不会影响原始数据。但通过传递参数的地址(即指针),可以使函数能够修改原始数据。

3.4、命名的返回值

使用命名返回值可以让函数返回的意图更明确,代码更整洁。命名的返回值在函数开始时就被初始化为零值,允许使用无参数的 return 语句返回已命名的变量。

示例:

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}
3.5、空白符 (blank identifier)

空白符 _ 用于忽略不需要的返回值,使得代码更加清晰。

_, err := someFunction()
if err != nil {
    log.Fatal(err)
}
3.6、改变外部变量

通过传递指针给函数,可以在不返回值的情况下修改外部变量,这对于处理大型数据或资源密集型操作特别有用。

示例:

func update(s *string) {
    *s = "updated"
}

这种方式在处理大型数据结构或需要明确修改原始数据的情况下非常有用,但应谨慎使用以避免不可预见的副作用。


4、函数调用

4.1、调用同一个包定义的函数

如果函数在同一个包中,只需要直接调用即可:

func add(a, b int) int  {
    return a + b
}
---
func main()  {
    fmt.Println(add(1, 2))  // 3
}
4.2、调用其他包定义的函数

如果函数是在不同的包中,需要先导入该函数所在的包,然后才能调用该函数,例如 Add 函数在 calculator 包中。

package calculator

func Add(a, b int) int  {
    return a + b
}

在 main 包中调用Add函数。

package main

import (
    "fmt"
    "calculator"
)
func main()  {
    fmt.Println(calculator.Add(1, 2))   // 3
}

注意:在调用其他包定义的函数时,只有这个函数名首字母大写的才可以被调用,例如函数名为 add 就会出现如下情况:

img

4.3、系统内置函数

Go 语言的内置函数非常实用,对于不同的使用场景提供了基本的功能支持,这样你就不需要额外引入包来处理基本操作。下面是你列出的一些内置函数的详细介绍和使用场景:

  1. close:

    • 用途: 在管道(channel)通信中关闭一个管道。
    • 注意: 只能关闭发送端的管道,接收端的管道不能关闭。尝试关闭一个已经关闭的管道或nil管道会引发panic。
  2. len, cap:

    • len:
      • 用途: 返回各种类型的长度,可以应用于字符串、数组、切片、字典和管道。
    • cap:
      • 用途: 返回数组、切片或管道的容量,即在重新分配之前能够保存的元素数量。
  3. new, make:

    • new:
      • 用途: 分配内存并返回指向该内存的指针,用于值类型和用户定义的类型,如结构体。
      • 示例: ptr := new(int),分配一个整型值的空间,其值为0(int的零值),ptr是指向该整型值的指针。
    • make:
      • 用途: 仅用于内置的引用类型(切片、字典、管道),分配内存并初始化一个对象,返回初始化的对象而不是指针。
      • 示例: s := make([]int, 10),创建一个长度为10的整型切片,并初始化所有元素为0。
  4. copy, append:

    • copy:
      • 用途: 用于复制和合并切片。它返回复制的元素数量,通常是源和目标切片中较小的长度。
      • 示例: copy(dst, src),将src切片中的元素复制到dst切片中。
    • append:
      • 用途: 向切片添加元素,并返回新的切片对象。如果原切片的容量不够将自动扩容。
      • 示例: s = append(s, 2),向切片s添加元素2。
  5. panic, recover:

    • panic:
      • 用途: 用于触发运行时异常,通常用于非正常的错误处理(比如检测到不应该发生的错误时)。
    • recover:
      • 用途: 用于恢复一个由panic引起的控制流,只有在defer函数中调用才有效。
  6. print, println:

    • 用途: 用于打印信息到控制台,不过在生产环境中推荐使用fmt包,因为fmt包提供了更丰富的格式化输出功能。
  7. complex, real, imag:

    • complex:
      • 用途: 创建一个复数。
      • 示例: c := complex(5, 6),创建一个实部为5,虚部为6的复数。
    • real, imag:
      • 用途: 分别返回复数的实部和虚部。
      • 示例: real(c), imag(c),返回复数c的实部和虚部。

这些函数的存在大大简化了 Go 语言编程的复杂度,让你能更快地实现功能。

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

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

相关文章

论文阅读:All-In-One Image Restoration for Unknown Corruption

发表时间:2022 cvpr 论文地址:https://openaccess.thecvf.com/content/CVPR2022/papers/Li_All-in-One_Image_Restoration_for_Unknown_Corruption_CVPR_2022_paper.pdf 项目地址:https://github.com/XLearning-SCU/2022-CVPR-AirNet 在本文…

Mysql使用中的性能优化——索引对插入操作的性能影响

当我们往表中插入数据时,如果表中有索引,则会给插入操作增加更多的工作量。带来的好处是可以提升查询效率。但是这种优劣该如何权衡,则需要通过数据对比来提供佐证。本文我们将对比没有索引、有一个普通索引、有一个唯一索引的性能差距。 结…

UniAnimate:华科提出人类跳舞视频生成新框架,支持合成一分钟高清视频

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

堆排序讲解

前言 在讲堆的删除时,我们发现一步一步删除堆顶的数据,排列起来呈现出排序的规律,所以本节小编将带领大家进一步理解堆排序。 1.堆排序概念 那么什么是堆排序? 堆排序(Heap Sort)是一种基于堆数据结构的排…

如何从 Android 图库中恢复误删除的照片

如果您正在阅读这篇文章,那么您肯定意外地从 Android 设备中删除了照片。并且您正在寻找一种简单的方法来恢复 Android 图库中已删除的照片。 从图库恢复已删除的照片 随着技术的进步,现在使用单个设备(即 Android 手机)&#xf…

vue27:脚手架详细介绍main.js

在 Vue.js 中,render 函数是一个可选的选项,它允许你自定义组件的渲染逻辑。 如果你没有在 Vue 实例中提供 render 函数,Vue 将使用模板(template)来生成虚拟 DOM。 以下是render / template 两种方式的比较&#…

C++ Qt实现http url启动本地应用程序

更多Qt文章,请访问《深入浅出C++ Qt开发技术专栏》:https://blog.csdn.net/yao_hou/category_9276099.html 文章目录 1、注册自定义协议2、编写web页面3、编写C++应用程序我们在使用腾讯会议时经常会通过http链接打开本地的腾讯会议,例如下图: 打开会议发起人给的链接,会出…

Python代码大使用Paramiko轻松判断文件类型,提取上级目录

哈喽,大家好,我是木头左! 一、Paramiko简介 Paramiko是一个用于SSHv2协议的Python实现,提供了客户端和服务器功能。它可以用于远程连接和管理服务器,执行命令、上传下载文件等。本文将介绍如何使用Paramiko判断文件类…

树莓派4B 零起点(二) 树莓派 更换软件源和软件仓库

目录 一、准备工作,查看自己的树莓派版本 二、安装HTTPS支持 三、更换为清华源 1、更换Debian软件源 2,更换Raspberrypi软件仓库 四、进行软件更新 接前章,我们的树莓派已经启动起来了,接下来要干的事那就是更换软件源和软件…

LeetCode ---400周赛

题目列表 3168. 候诊室中的最少椅子数 3169. 无需开会的工作日 3170. 删除星号以后字典序最小的字符串 3171. 找到按位与最接近 K 的子数组 一、候诊室中的最少椅子数 简单的模拟题,我们可以这样来模拟:当有顾客来时,我们加一把椅子&…

java并发控制(猴子摘桃例子)

【问题】 有n个桃子, 猴子A每次固定摘2个,猴子B每次固定摘3个,这2只猴子不断摘桃子直到剩余桃子数量不足以摘(必须满足摘桃个数); 【1】 使用AtomicInteger(推荐) 1)利…

11 深入理解Linux文件系统与日志分析

目录 11.1 深入理解Linux文件系统 11.1.1 inode与block详解 1. inode和block概述 2. inode的内容 3. inode的号码 4. inode的大小 11.1.2 硬链接与软连接 1. 硬链接 2. 软连接 11.1.3 EXT类型文件恢复 1. 编译安装extundelete 2. 模拟删除并执行恢复操作 11.1.4 xfs类型文件备…

【Centos】深度解析:CentOS下安装pip的完整指南

【Centos】深度解析:CentOS下安装pip的完整指南 大家好 我是寸铁👊 总结了一篇【Centos】深度解析:CentOS下安装pip的完整指南✨ 喜欢的小伙伴可以点点关注 💝 方式1(推荐) 下载get-pip.py到本地 sudo wget https://bootstrap.p…

用python编撰一个电脑清理程序

自制一个电脑清理程序,有啥用呢?在电脑不装有清理软件的时候,可以解决自己电脑内存不足的情况。 1、设想需要删除指定文件夹中的临时文件和缓存文件。以下是代码。 import os import shutil def clean_folder(folder_path): for root,…

CleanMyMac X 4.15.4破解版含2024最新CleanMyMac激活码

CleanMyMac X 4.15.4 for Mac 2023最新中文激活版是一款mac电脑清理工具,可让您快速轻松地个性化您的 Mac 操作系统。此外,它提高了系统速度和性能,帮助你远离黑客和垃圾文件。该程序具有尖端工具和功能,能够清除Mac系统中的任何故…

利用SuperGlue算法实现跨尺度金字塔特征点的高效匹配(含py代码)

在计算机视觉领域,特征点匹配是一个基础而关键的任务,广泛应用于图像拼接、三维重建、目标跟踪等方向。传统的特征点匹配方法通常基于相同尺度下提取的特征进行匹配,然而在实际场景中,由于成像距离、分辨率等因素的差异&#xff0…

图像处理ASIC设计方法 笔记29 场景自适应校正算法

P152 7.2.3 场景自适应校正算法 (一)Scribner提出的神经网络非均匀性校正算法 非均匀性校正(Non-Uniformity Correction,简称NUC)算法是红外成像技术中非常重要的一个环节。它主要用于校正红外焦平面阵列(Infrared Focal Plane Arrays,简称IRFPA)中的固定模式噪声,以提…

《python程序语言设计》2018版第5章第46题均值和标准方差-下部(本来想和大家说抱歉,但成功了)

接上回,5.46题如何的标准方差 本来想和大家说非常抱歉各位同学们。我没有找到通过一个循环完成两个结果的代码。 但我逐步往下的写,我终于成功了!! 这是我大前天在单位找到的公式里。x上面带一横是平均值。 我不能用函数的办法…

高考后志愿填报信息采集系统制作指南

在高考的硝烟散去之后,每位学生都面临着一个重要的任务——志愿填报。老师们如何高效、准确地收集和整理这些信息,成为了一个棘手的问题。难道我们只能依赖传统的手工登记方式,忍受其繁琐和易错吗? 易查分是一个简单易用的在线工具…

基于GTX 8B10B编码的自定义PHY上板测试(高速收发器十四)

前文整理了GTX IP,完成了自定义PHY协议的收发模块设计,本文将通过光纤回环,对这些模块上板测试,首先需要编写一个用于生成测试数据的用户模块。 1、测试数据生成模块 本模块用于生成自定义PHY协议的测试数据,通过axi_…