本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是golang专题的第八篇,我们来聊聊golang当中的函数。

我们在之前的时候已经介绍过了函数的基本用法,知道了怎么样设计或者是定义一个函数,以及怎么样调用一个函数,还了解了defer的用法。今天这篇文章我们来继续深入这个话题,来看看golang当中关于函数的一些进阶的用法。

返回error

前文当中我们曾经提到过,在golang当中并没有try catch捕获异常的机制。在其他语言当中异常只有一种,可以通过try catch语句进行捕获,而golang当中做了区分,将异常分为两种,一种是可以在函数当中返回的error,另外一种是严重的会引起程序崩溃的panic

在golang中,error也是一个数据类型,由于golang支持函数的多值返回,所以我们可以设置一个返回值是error。我们通过对这个error的判断来获取运行函数的情况。

举个例子,比如说,假设我们实现一个Divide函数实现两个int相除。那么显然我们需要除数不能为0,当除数为0的时候我们需要返回一个异常。这个时候我们可以把代码写成这样:

// Divide test
func Divide(a, b int) (ret int, err error) {
if b == 0 {
err = errors.New("divisor is zero")
return
}
return a / b, nil
}

当我们调用函数的时候,我们用两个变量去接收这个函数返回的结果,第二个变量的类型是error。当这个函数成功执行的时候第二个变量的结果为nil,我们只需要判断它是否等于nil,就可以知道函数执行是否成功。如果不成功,我们还可以记录失败的原因。

func main() {
ret, err := Divide(5, 2)
if err == nil {
fmt.Println(ret)
} else {
fmt.Println(err)
}
}

这种用法在golang当中非常常见,我们之前在介绍字符串相关操作的时候也介绍过返回error的用法。我们在设计函数的时候如果需要判断输入的合法性可以使用error,这样就可以保证handle住非法的情况,并且也能让下游感知到。

不定参数

不定参数的用法在很多语言当中都有,比如在Python当中,不定参数是*args。通过*args我们可以接受任何数量的参数,由于Python是弱变量类型的语言,所以args这些参数的类型可以互不相同。但是golang不行,golang严格限制类型,不定参数必须要保证类型一样。除此之外,其他的用法和Python一样,不定参数会以数组的形式传入函数内部,我们可以使用数组的api进行访问。

我们来看一个例子,我们通过...来定义不定参数。比如我们可以实现一个sum函数,可以将任意个int进行累加。

func Sum(nums ... int) int{
ret := 0
for _, num := range nums {
ret += num
}
return ret
}

我们来仔细研究一下上面这个例子,在这个例子当中,我们通过...传入了一个不定参数,我们不定参数的类型只写一次,写在...的后面。从底层实现的机制上来说,不定参数本质上是将传入的参数转化成数组的切片。但是这就有了一个问题,既然传入的是一个数组的切片,我们为什么要专门设置一个关键字,而不是规定传入一个切片呢?

比如上面的代码我们完全可以写成这样:

func Sum(nums []int) int{
ret := 0
for _, num := range nums {
ret += num
}
return ret
}

无论从代码的阅读还是编写上来看相差并不大,好像这样做完全没有意义,其实不是这样的。这个关键字简化的并不是函数的设计方,而是函数的使用方。如果我们规定了函数的输入是一个切片,那么当我们在传入数据的时候,必须要使用强制转化,将我们的数据转化成切片,比如这样:

Sum([]int(3, 4, 6, 8))

而使用...关键字我们则可以省略掉强制转化的过程,上面的代码我们写成这样就可以了:

Sum(3, 4, 6, 8)

很明显可以看出差异,使用不定参数的话调用方会轻松很多,不需要再进行额外的转换。如果我们要传入的也是一个数组,那么在传递的时候也需要用...符号将它展开

a := make([]int)
a = append(a, 3)
a = append(a, 4)
Sum(a...)
Sum(a[1:]...)

既然聊到不定参数的传递,那么又涉及到了一个问题,当我们想要像Python那样传递多个类型不同的参数的时候,应该怎么办呢?按照道理golang是静态类型的语言,限制死了参数的类型,是不能随便转换的才对。但是偏偏这样操作是可以的,因为golang当中有一个特殊的类型,叫做interface

interface的用法很多,一个很重要的用法是用在面向对象当中充当结构体的接口。这里我们不做过多深入,我们只需要知道,interface的一个用法是可以用来代替所有类型的变量。我们来看一个例子:

func testInterface(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println("it's a int")
case string:
fmt.Println("it's a string")
case float32:
fmt.Println("it's a float")
default:
fmt.Println("it's an unknown type")
}
}
} func main() {
testInterface(3, 4.5, "abc")
}

我们可以用.(type)获取一个interface变量实际的类型,这样我们就实现了任意类型任意数量参数的传入。

匿名函数和闭包

匿名函数我们在Python当中经常使用到,其实这个概念出现已久,最早可以追溯到1958年Lisp语言。所以这并不是一个新鲜的概念,只是传统的C、C++等语言没有支持匿名函数的功能,所以显得好像是一个新出现的概念一样。golang当中也支持匿名函数,但是golang当中匿名函数的使用方式和Python等语言稍稍有些不同。

在Python当中我们是通过lambda关键字来定义匿名函数,它可以被传入另一个函数当中,也可以赋值给一个变量。golang当中匿名函数的定义方式和普通函数基本是一样的,只是没有函数名而已,不过它也可以被传入函数或者是赋值给另一个变量。

比如:

s := func(a, b int) int {
return a + b
} c := s(3, 4)

除了匿名函数之外,golang还支持闭包。闭包的概念我们在之前Python闭包的介绍当中曾经提到过,我们之前也用过好几次,闭包的本质不是一个包,而是一个函数,是一个持有外部环境变量的函数。比如在Python当中,我们经常可以看到这样的写法:

def outside(x):
def inside(y):
print(x, y)
return inside ins = outside(3)
ins(5) #3, 5

我们可以看到outside这个函数返回了inside这个函数,对于inside这个函数而言,它持有了x这个变量。x这个变量并不是属于它的,而是定义在它的外部域的。并且我们在调用inside的时候是无法干涉这个变量的,这就是一个闭包的典型例子。根据轮子哥的说法,闭包的闭的意思并不是封闭内部,而是封闭外部。当外部scope失效的时候,函数仍然持有一份外部的环境的值。

golang当中闭包的使用方法大同小异,我们来看一个类似的例子:

func main() {
a := func(x int) (func(int)) {
return func(y int){
fmt.Println(x, y)
}
}
b := a(4)
b(5)
}

这个闭包的例子和刚才上面Python那个例子是一样的,唯一不同的是由于golang是强类型的语言,所以我们需要在定义闭包的时候将输入和输出的类型定义清楚。

总结

关于golang当中函数的高级用法就差不多介绍完了,这些都是实际编程当中经常使用的方法,如果想要学好golang这门语言的话,这些是基本功。如果你之前有其他语言的基础,来写go的话,整体上手的难度还是不大的,很多设计都可以在其他的语言当中找到影子,有了参照来学会简单得多。

我很难描述实际工作当中写golang的体验,和我写任何一门其他的语言都不一样,有一种一开始期望很低,慢慢慢慢总能发现惊喜的感觉。我强烈建议大家去实际感受一下。

如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

本文使用 mdnice 排版

Golang简单入门教程——函数进阶篇的更多相关文章

  1. 携程Apollo简单入门教程这一篇就够了

    1. Apollo背景 对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境.分集群管理配置,完善的权限.审核机制……   废话不多说,参考官方文档   如果不想看文档, 也没关系, 跟 ...

  2. Linux Capabilities 入门教程:进阶实战篇

    原文链接:https://fuckcloudnative.io/posts/linux-capabilities-in-practice-2/ 该系列文章总共分为三篇: Linux Capabilit ...

  3. NumPy简单入门教程

    # NumPy简单入门教程 NumPy是Python中的一个运算速度非常快的一个数学库,它非常重视数组.它允许你在Python中进行向量和矩阵计算,并且由于许多底层函数实际上是用C编写的,因此你可以体 ...

  4. Systemd 入门教程:实战篇

    Systemd 入门教程:实战篇 上一篇文章,介绍了 Systemd 的主要命令,这篇文章主要介绍如何使用 Systemd 来管理我们的服务,以及各项的含义: 一.开机启动 对于那些支持 System ...

  5. 【转帖】Systemd 入门教程:命令篇

    Systemd 入门教程:命令篇  Copy From http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html 感觉 ...

  6. Linux Capabilities 入门教程:概念篇

    原文链接:Linux Capabilities 入门教程:概念篇 Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限.root 用户拥有 ...

  7. 程序员,一起玩转GitHub版本控制,超简单入门教程 干货2

    本GitHub教程旨在能够帮助大家快速入门学习使用GitHub,进行版本控制.帮助大家摆脱命令行工具,简单快速的使用GitHub. 做全栈攻城狮-写代码也要读书,爱全栈,更爱生活. 更多原创教程请关注 ...

  8. GitHub这么火,程序员你不学学吗? 超简单入门教程 【转载】

    本GitHub教程旨在能够帮助大家快速入门学习使用GitHub. 本文章由做全栈攻城狮-写代码也要读书,爱全栈,更爱生活.原创.如有转载,请注明出处. GitHub是什么? GitHub首先是个分布式 ...

  9. Linux 命令详解(八)Systemd 入门教程:实战篇

    Systemd 入门教程:实战篇 http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

随机推荐

  1. HDU2825AC自动机+状压

    //我感觉这题不如叫你不看代码就不知道题干在说啥,强烈吐槽 Liyuan lives in a old apartment. One day, he suddenly found that there ...

  2. Getting Started With Node and NPM

    Getting Started with Node and NPM Let's start with the basics. Install Node.js: https://nodejs.org.

  3. Qt版本中国象棋开发(三)

    实现功能:棋子初始化及走棋规则 棋子类: #ifndef STONE_H #define STONE_H #include <QString> class Stone { public: ...

  4. [SD心灵鸡汤]004.每月一则 - 2015.08

    1.事常与人违,事总在人为. 2.骏马是跑出来的,强兵是打出来的. 3.驾驭命运的舵是奋斗.不抱有一丝幻想,不放弃一点机会,不停止一日努力. 4.如果惧怕前面跌宕的山岩,生命就永远只能是死水一潭. 5 ...

  5. (五)Ajax修改购物车单品数量

    需要gson-2.2.4.jar BookServlet.java package com.aff.bookstore.servlet; import java.io.IOException; imp ...

  6. jQuery-操作元素的内容,属性,样式

    1.操作内容 获取: 双标签:html() input:val() 设置: 双标签:html('新内容') input:val('新内容') 2.操作属性 * 获取:attr('属性名') * 设置: ...

  7. Rocket - config - View

    https://mp.weixin.qq.com/s/b5o3s2DgqOz3-iK8FqPeLQ   介绍配置相关的基础类及其继承关系.   参考链接: https://github.com/fre ...

  8. [精华帖]Java接口怎么定义?如何使用?【实例讲解】

    [精华帖?]滑稽之谈||| 题目: 模拟电脑USB功能设备使用 1.定义USB接口,具备最基本的开启功能和关闭功能 2.定义电脑类,具有开机.关机以及使用usb设备功能 3.鼠标类.具有usb功能,并 ...

  9. Sublime Text3 注册码(Windows/Build 3176版本)| 开发工具

    转自:dushusir.com 1.修改hosts文件(路径:C:\Windows\System32\drivers\etc): 0.0.0.0 www.sublimetext.com 0.0.0.0 ...

  10. 容器技术之Dockerfile(二)

    前文我们聊到了什么是dockerfile,它的主要作用以及dockerfile的一些基本指令的使用方法,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13019 ...