大家好,我是煎鱼。

在 Go 语言中,一提函数,大家提的最多的就是 “Go 语言的函数是一等公民”。这个定义来的非常突然,我们先了解一下什么是一等公民,他又凭什么?

根据维基百科的一等公民(First-class citizen)的定义:

In programming language design, a first-class citizen (also type, object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable.

在编程语言设计中,给定编程语言中的一等公民(也就是类型,对象,实体或值)可以把函数赋值给变量,也可以把函数作为其它函数的参数或者返回值来直接使用。

Go 语言的函数也满足这个定义,因此常被称为 “一等公民”,非常有意思。了解清楚背景后,接下来进一步展开。

普通函数

在 Go 语言中普通函数的定义格式为 func [函数名](入参)(出参),如下:

func callFuncA(x, y string) (s string, err error) {return x + y, nil
}func main() {callFuncA("炸", "煎鱼")
}

在示例代码中声明了一个函数名为 callFuncA 的方法,他只允许在包内调用,因此首字母为小写。

其具有两个入参,分别是 xy,类型都为 string。而出参为变量 serr,类型分别为 stringerror

另外在函数体内返回值时,也可以采用快捷返回的方式:

func callFuncA(x, y string) (s string, err error) {s = x + yreturn
}

在出参时所声明的变量名称,是可以应用到自身函数的。因此若直接执行 return 则会隐式返回已经声明的出参变量。

在函数定义时,其入参还支持可变参数的语法:

func callFuncA(x ...string) (s string, err error) {s = strings.Join(x, ",")return
}func main() {fmt.Println(callFuncA("炸", "煎鱼"))
}

在入参变量上声明为 x ...string,则表示变量 xstring 类型的可变变量,能够在入参时传入多个 string 参数。

可变变量所传入的格式为切片(slice)类型,该类型我们会在后面的章节进行讲解,你可以理解为不受长度限制的动态数组:

[0: 炸 1: 煎鱼]

一般对可变变量的常见后续操作多是循环遍历处理,又或是进行拼接等操作。

匿名函数

Go 语言也默认支持匿名函数的声明,声明的方式与普通函数几乎一样:

func main() {s := func(x, y string) (s string, err error) {return x + y, nil}s("炸", "煎鱼")
}

匿名函数可以在任意地方声明,且不需要定义函数名,如果在函数体后马上跟 () 则表示声明后立即执行:

func main() {s, _ := func(x, y string) (s string, err error) {return x + y, nil}("炸", "煎鱼")
}

而在所有的函数类使用中,有一点非常重要,那就是函数变量作用域的理解:

func main() {x, y := "炸", "煎鱼"s, _ := func() (s string, err error) {return x + y, nil}()fmt.Println(s)
}

该匿名函数没有形参,函数内部没有定义相应的变量,此时其读取的是全局的 xy 变量的值,输出结果是 “炸煎鱼”。

func main() {x, y := "炸", "煎鱼"_, _ = func(x, y string) (s string, err error) {x = "吃"return x + y, nil}(x, y)fmt.Println(x, y)
}

该匿名函数有形参,但是在函数内部又重新赋值了变量 x。那么最终外部所输出的变量 x 的值是什么呢?输出结果是 “炸 煎鱼”。

为什么明明在函数内已经对变量 x 重新赋值,却依然没有改变全局变量 x 的值呢?

其本质原因是作用域不同,函数内部所修改的变量 x 是函数内的局部变量。而外部的是全局的变量,所归属的作用域不同。

结构方法

在结合结构体(struct)的方式下,可以声明归属于该结构体下的方法:

type T struct{}func NewT() *T {return &T{}
}func (t *T) callFuncA(x, y string) (s string, err error) {return x + y, nil
}func main() {NewT().callFuncA("炸", "煎鱼")
}

具体的函数的使用方法与普通函数一样,无其他区别。

而与结构体有关的值传递、引用传递的方法调用将在具体后面的章节再展开。

内置函数

Go 语言本身有支持一些内置函数,这些内置函数的调用不需要引用第三方标准库。内置函数的作用是用于配合 Go 语言的常规使用,数量非常少。如下:

  • 用于获取某些类型的长度和容量:len、cap。

  • 用于创建并分配某些类型的内存:new、make。

  • 用于错误处理机制(异常恐慌、异常捕获):panic、recover。

  • 用于复制和新增切片(slice):copy、append。

  • 用于简单输出信息:print、println。

  • 用于处理复数:complex、real、imag。

针对每个内置函数的真实使用场景,我们会在后续的章节再进一步展开,因为每个内置函数本质上都对应着各类型的使用场景。

总结

在本章节中,我们介绍了 Go 语言的函数为什么称是一等公民,并且针对函数的各类变形:普通函数、匿名函数、结构方法、内置函数进行了基本的说明。

面对新手入门最容易犯错的函数作用域问题,也进行了基本的梳理。这块建议大家要多多深入思考、理解,避免日后踩坑。


关注煎鱼公众号,吸取精华:

???? 点击关注煎鱼,在知识的海洋里遨游

学习资料分享,关注公众号回复指令:

  • 回复【000】,下载 LeetCode 题解大全。

  • 回复【001】,下载 Go 进阶图书 Mastering Go。

Go 语言中的一等公民:看似普通的函数,凭什么?相关推荐

  1. Go语言系列——31-自定义错误、32-panic和recover、33-函数是一等公民(头等函数)、34-反射、35-读取文件、36-写入文件

    文章目录 31-自定义错误 使用 New 函数创建自定义错误 使用 Errorf 给错误添加更多信息 使用结构体类型和字段提供错误的更多信息 使用结构体类型的方法来提供错误的更多信息 32-panic ...

  2. 作为一等公民的 Python 函数

    文章目录 参考 描述 一等公民 概念 Python 中的一等公民 一切皆对象 人人平等 作为一等公民的 Python 函数 作为函数的参数进行传递 作为函数的返回值进行返回 作为数据结构中的元素 能够 ...

  3. c语言 字符串 strncpy,详解c语言中的 strcpy和strncpy字符串函数使用

    详解c语言中的 strcpy和strncpy字符串函数使用 strcpy 和strcnpy函数--字符串复制函数. 1.strcpy函数 函数原型:char *strcpy(char *dst,cha ...

  4. 在C语言中以编程的方式获取函数名

    在C语言中以编程的方式获取函数名 仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易导致错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧. 对象反射库.调试工具及 ...

  5. 关于linuxC语言中创建进程,利用execlp函数执行.c程序问题

    关于linuxC语言中创建进程,利用execlp函数执行.c程序问题 对于一个小白来说,这个系统调用真的太难了,完全摸不着头脑啊,一搜execlp函数,全是执行的命令,什么传参硬是没懂. 不过说到底, ...

  6. C语言中的二等公民在C++是如何翻身的?

    在原来的C语言中,enum.const.auto等关键字都不尽完善,并没有发挥应有的功能,是二等公民,而随着时间的推移,在C++中他们都被完善了,有些还被赋予了新的功能,担当起了重要的角色.相反,原本 ...

  7. python 一等公民_Python中的一等公民

    一流公民是指能够为促进其他同伴实体的所有运营提供支持的实体. 这些实体经常使用:在传递参数时,从函数中返回值,条件修改和值赋值. 在本文中,我们将研究Python 3.x或更早版本中一流公民的实现和使 ...

  8. c语言中用于获取字符串长度的函数是,C语言中求字符串长度的函数的几种实现方法...

    C语言中求字符串长度的函数的几种实现方法 1.最常用的方法是创建一个计数器,判断是否遇到'\0',不是'\0'指针就往后加一. int my_strlen(const char *str) { ass ...

  9. R语言中的异常处理和eval函数

    异常处理tryCatch() [R]R语言里的异常处理与错误控制 看看下面这个例子就能快速了解. #这里不要用c(1,2,'a',4), 因为R中向量中的元素必须一致,会自动转换成c("1& ...

最新文章

  1. evans pde 第三章_智慧树报关实务第三章答案
  2. 【JS】逻辑运算符 非! 与 或||(处理对象时注意)
  3. ipsec *** 的总结性谈论
  4. android 视网膜黄斑检查 amsler,自测黄斑病变 | 一张图搞定
  5. 免费发布一个简单而有趣的计算工具
  6. mybatis-plus实现乐观锁
  7. 【Vue】基础入门 —— 黑马程序员
  8. Android SDK ADB命令行总结
  9. Android 炫酷自定义 View - 剑气加载
  10. 以核心素养为导向的计算机教学方式,核心素养导向的课堂教学-感悟
  11. PhoneWindowManager().interceptKeyBeforeQueueing()中的interactive变量值的来源
  12. 深度学习+语音,基础普及篇笔记(一)
  13. 【MQTT基础篇(五)】发布、订阅和取消订阅
  14. 奥特曼宇宙英雄服务器维修,奥特曼宇宙英雄1.1.7版本
  15. SQL Server 重新组织生成索引
  16. 以太坊(Ethereum) - 网络节点
  17. 经典C语言程序设计100例,部分有个人注解
  18. 地平线【OpenExplorer天工开物】开发平台
  19. 四川大学计算机导论吴承恩,四川大学计算机导论期末例题
  20. 一款好看的iapp捐赠榜单源码

热门文章

  1. 使用Piwigo管理您的照片
  2. 宏基笔记本预装软件中英文对照表
  3. android布局黑色字体颜色,Android 沉浸式状态栏-字体颜色与背景颜色修改实现与兼容...
  4. [Mac]苹果系统安装burpsuite 2.0.11
  5. ssm框架的企业固定资产管理系统
  6. C#学习1.string与StringBuilder
  7. CAD看图软件如何画线?
  8. 华为认证考试需要准备什么
  9. Scratch(三十四):古诗大作战
  10. PAT甲级A1013. Battle Over Cities (25)