函数是基于功能或者逻辑进行聚合的可复用的代码块。将一些复杂的、冗长的代码抽离封装成多个代码片段,即函数,有助于提高代码逻辑的可读性和可维护性。不同于Python,由于 Go lang是编译型语言,编译之后再运行,所以函数的定义顺序无关痛痒。

函数声明

在 Go lang里,函数声明语法如下:

func function_name(parameter_list) (result_list) {
//函数逻辑
}

这里使用function的简写形式 func关键词,后面依次接 function_name(函数名) , parameter_list(参数列表) , result_list(返回值列表)以及函数体 。

parameter_list(参数列表)成员:函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供,函数中的参数列表和返回值并非是必须的。

result_list(返回值列表):函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。

如果有连续若干个参数的类型一致,那么只需在最后一个参数后添加该类型:

package main  

import "fmt"  

// 函数返回一个无名变量,返回值列表的括号省略
func sum(x int, y int) int {
return x + y
} // 无参数列表和返回值
func printBookName() {
fmt.Println("go lang1.18")
} // 参数的类型一致,只在最后一个参数后添加该类型
func sub(x, y int) int {
return x - y
} func main() {
fmt.Println("1 + 1 = ", sum(1, 1))
printBookName()
fmt.Println("2 - 1 =", sub(2, 1))
}

程序返回:

command-line-arguments
1 + 1 = 2
go lang1.18
2 - 1 = 1

不定长参数

和Python一样,Go lang也支持不定长参数,即参数有多少个并不确定的情况。

在参数类型前面加 ... 表示一个切片,用来接收调用者传入的参数。注意,如果该函数下有其他类型的参数,这些其他参数必须放在参数列表的前面,切片必须放在最后:

package main  

import "fmt"  

func show(args ...string) int {
sum := 0
for _, item := range args {
fmt.Println(item)
sum += 1
}
return sum
} func main() {
fmt.Println(show("1", "2", "3"))
}

和Python的*args用法差不多,但需要注意必须要声明函数的数据类型,程序返回:

1
2
3
3

如果传多个参数的数据类型都不一样,可以指定类型为 ...interface{} ,然后再进行遍历:

package main  

import "fmt"  

func PrintType(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "type is int.")
case string:
fmt.Println(arg, "type is string.")
case float64:
fmt.Println(arg, "type is float64.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
} func main() {
PrintType(1, 3.1415, "go lang 1.18")
}

此外,还可以使用 ... 可以用来解序列,能将函数的可变参数(即切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身:

package main  

import "fmt"  

func main() {
var s []string
s = append(s, []string{"1", "2", "3"}...)
fmt.Println(s)
}

这里将字符串切片取出来后,传递给内置的append方法,程序返回:

[1 2 3]

函数的返回值

一个函数可以没有返回值,也可以有一个返回值,也可以有返回多个值:

package main  

import "fmt"  

func swap(x, y string) (string, string) {
return y, x
} func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A + B
Multiplied = A * B
return
} func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b) fmt.Println(SumAndProduct(1, 2)) }

程序返回:

Kumar Mahesh
3 2

_ 是Go lang里的空白标识符。它可以代替任何类型的任何值。我们可以利用它来忽略某些别人会用到但我们不会用到的函数返回值:

package main  

import (
"fmt"
) func rectProps(length, width float64) (float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, _ := rectProps(10.8, 5.6) // perimeter is discarded
fmt.Printf("Area %f ", area)
}

程序返回:

Area 60.480000

匿名函数

有点类似Python中的lambda表达式,但实际上并不是作为语法糖而存在:

package main  

import (
"fmt"
) func main() {
f := func() {
fmt.Println("hello world")
}
f() //hello world
fmt.Printf("%T\n", f) //打印 func()
}

程序返回:

hello world
func()

一望而知,只是匿名而已,但通过变量可调用,另外也可以拥有参数:

package main  

import (
"fmt"
)
func main() {
f:=func(args string){
fmt.Println(args)
}
f("hello world")//hello world
//或
(func(args string){
fmt.Println(args)
})("hello world")//hello world
//或
func(args string) {
fmt.Println(args)
}("hello world") //hello world
}

程序返回:

hello world
hello world
hello world

基本上,匿名函数和命名函数用法上并无二致。

闭包(closure)

很多语言都有闭包的概念,简单理解就是函数的嵌套:

package main  

import "fmt"  

func main() {
a := Fun()
b:=a("hello ")
c:=a("hello ")
fmt.Println(b)//worldhello
fmt.Println(c)//worldhello hello
}
func Fun() func(string) string {
a := "world"
return func(args string) string {
a += args
return a
}
}

程序返回:

worldhello
worldhello hello

这里我们将方法作为参数传递到方法内部执行,这样内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。

延迟函数

延迟其实是延迟(defer)语句,延迟语句被用于执行一个函数调用,在这个函数之前,延迟语句返回:

package main  

import "fmt"  

func main() {
a := 1
b := 2
defer fmt.Println(b)
fmt.Println(a)
}

程序返回:

1
2

说白了就是一种倒装的形式,非延迟语句先执行,最后再执行延迟语句。

延迟也并不仅仅局限于函数内部语句,延迟一个方法调用也是可以的:

package main  

import (
"fmt"
) type person struct {
firstName string
lastName string
} func (p person) fullName() {
fmt.Printf("%s %s", p.firstName, p.lastName)
} func main() {
p := person{
firstName: "go lang",
lastName: "python",
}
defer p.fullName()
fmt.Printf("Welcome ")
}

程序返回:

Welcome go lang python

初始化函数

顾名思义,和Python中的魔法方法init一样,可以提前做一些初始化操作:

package main  

import "fmt"  

var a int = initVar()  

func init() {
fmt.Println("init2")
} func init() {
fmt.Println("init")
} func initVar() int {
fmt.Println("init var...")
return 100
} func main() {
fmt.Println("main...")
}

程序返回:

init var...
init2
init

这里的初始化顺序是:变量初始化->init()->main()

和Python不同的是,每个包可以有多个初始化函数。

结语

归根结底,函数可以被认为是Go lang中的一种数据类型,可以作为另一个函数的参数,也可以作为另一个函数的返回值,使用起来相当灵活,但我们也不能矫枉过正,毫无节制地用函数封装逻辑,造成过度封装的现象。

化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07的更多相关文章

  1. 仙人指路,引而不发,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中New和Make函数的使用背景和区别EP16

    Golang只有二十五个系统保留关键字,二十几个系统内置函数,加起来只有五十个左右需要记住的关键字,纵观编程宇宙,无人能出其右.其中还有一些保留关键字属于"锦上添花",什么叫锦上添 ...

  2. 延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17

    先行定义,延后执行.不得不佩服Go lang设计者天才的设计,事实上,defer关键字就相当于Python中的try{ ...}except{ ...}finally{...}结构设计中的finall ...

  3. 清源正本,鉴往知来,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中引用类型是否进行引用传递EP18

    开篇明义,Go lang中从来就不存在所谓的"引用传递",从来就只有一种变量传递方式,那就是值传递.因为引用传递的前提是存在"引用变量",但是Go lang中从 ...

  4. 你有对象类,我有结构体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06

    再续前文,在面向对象层面,Python做到了超神:万物皆为对象,而Ruby,则干脆就是神:飞花摘叶皆可对象.二者都提供对象类操作以及继承的方式为面向对象张目,但Go lang显然有一些特立独行,因为它 ...

  5. 百亿数据百亿花, 库若恒河沙复沙,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang数据库操作实践EP12

    Golang可以通过Gorm包来操作数据库,所谓ORM,即Object Relational Mapping(数据关系映射),说白了就是通过模式化的语法来操作数据库的行对象或者表对象,对比相对灵活繁复 ...

  6. 层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10

    Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就"导包"就行了,无论是内部的还是外部的,使用im ...

  7. 兔起鹘落全端涵盖,Go lang1.18入门精炼教程,由白丁入鸿儒,全平台(Sublime 4)Go lang开发环境搭建EP00

    Go lang,为并发而生的静态语言,源于C语言又不拘泥于性能,高效却不流于古板,Python灵活,略输性能,Java严谨,稍逊风骚.君不见各大厂牌均纷纷使用Go lang对自己的高并发业务进行重构, ...

  8. 因势而变,因时而动,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang泛型(generic)的使用EP15

    事实上,泛型才是Go lang1.18最具特色的所在,但为什么我们一定要拖到后面才去探讨泛型?类比的话,我们可以想象一下给小学一年级的学生讲王勃的千古名篇<滕王阁序>,小学生有多大的概率可 ...

  9. 巨细靡遗流程控制,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang流程结构详解EP09

    流程结构就是指程序逻辑到底怎么执行,进而言之,程序执行逻辑的顺序.众所周知,程序整体都是自上由下执行的,但有的时候,又不仅仅是从上往下执行那么简单,大体上,Go lang程序的流程控制结构一共有三种: ...

随机推荐

  1. JavaSE_多线程入门 线程安全 死锁 状态 通讯 线程池

    1 多线程入门 1.1 多线程相关的概念 并发与并行 并行:在同一时刻,有多个任务在多个CPU上同时执行. 并发:在同一时刻,有多个任务在单个CPU上交替执行. 进程与线程 进程:就是操作系统中正在运 ...

  2. 一分钟学会如何自定义小程序轮播图(蜜雪冰城Demo)

    最近开发小程序项目用到了轮播图,默认的有点单调,作为后端程序员,研究一番最终实现了.本文会从思路,代码详细介绍,相信读过此文后,不管以后在开发中碰到轮播图还是需要自定义修改其他的样式都可以按这个思路解 ...

  3. Spring IOC源码研究笔记(2)——ApplicationContext系列

    1. Spring IOC源码研究笔记(2)--ApplicationContext系列 1.1. 继承关系 非web环境下,一般来说常用的就两类ApplicationContext: 配置形式为XM ...

  4. MVC - forward 和 redirect 的区别

    MVC - forward 和 redirect 的区别 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服 ...

  5. CentOS8设置国内镜像源(阿里云镜像)

    CentOS8设置国内镜像源(阿里云) 1.备份原有配置 [root@localhost ~]# mkdir /etc/yum.repos.d.bak [root@localhost ~]# mv / ...

  6. 使用PowerShell压缩和解压ZIP包

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月13日. 解压ZIP包 使用PowerShell的Expand-Archive命令.PowerShell官方文档地址. 命令格式: ...

  7. python常用标准库(压缩包模块zipfile和tarfile)

    常用的标准库 在我们常用的系统windows和Linux系统中有很多支持的压缩包格式,包括但不限于以下种类:rar.zip.tar,以下的标准库的作用就是用于压缩解压缩其中一些格式的压缩包. zip格 ...

  8. sqlserver用windows方式验证登录踩过的坑

    坑位一: 之前没用过windows验证方式登录sqlserver,连接串怎么写 坑位二: 链接上了,但是启动报错 八月 19, 2020 9:33:43 上午 com.microsoft.sqlser ...

  9. Vue回炉重造之图片加载性能优化

    前言 图片加载优化对于一个网站性能好坏起着至关重要的作用.所以我们使用Vue来操作一波.备注 以下的优化一.优化二栏目都是我自己封装在Vue的工具函数里,所以请认真看完,要不然直接复制的话,容易出错的 ...

  10. SLF4J 日志门面

    目录 01.简单介绍 02.日志级别 03.入门案例 03.动态打印 04.异常打印 05.日志集成 06.集成 logback 07.集成 slf4j-nop 08.集成 log4j 09.集成 j ...