一:函数

1 概述:

函数是 Go 程序源代码的基本构造单位,一个函数的定义包括如下几个部分,函数声明关键字 也町、 函数名、参数列表、返回列表和函数体。
函数名遵循标识符的命名规则, 首字母的大小写决定该函数在其他包的可见性:大写时其他包可见,小写时只有相同的包可以访问;
func 函数名 (参数列表) (返回值列表) {
函数体
}

2 特点

函数声明的格式

方法名首字母大写是public,方法名首字母小写private私有方法

1)函数类型

package main

import (
"fmt"
)
//1 无参无返回值
func test(){
fmt.Println("三无产品")
}
//2 有参无返回值
func test1(v1 int,v2 int){
fmt.Println(v1,v2)
}
//3 有不定参数无返回值
func test2(args ...int){
for _,n :=range args{
fmt.Println(n)
}
} //调用以上函数
func main(){
test()
test1(,)
test2(,,,)
}
package main

import "fmt"

//无参有返回值
func test4(a int ,str string){
a = 666
str= "我是沙雕"
return
}
func test5() (int,string){
return 250,"字符串"
}
func main(){
a,str := test4() //test4没有返回值,运行会报错
fmt.Println(a,str)
_,s :=test5()
fmt.Println(s)
}
package main

import (
"fmt"
)
//有参有返回值
//求两个数的最大值和最小值
func test1(num1 int,num2 int) (min int,max int){
if num1>num2{
min=num2
max=num1
}else {
max=num2
min=num1
}
return
}
func main(){
min,max := test1(,)
fmt.Println(min,max)
}

求1~100的和代码实现两种方法

package main

import "fmt"
//循环实现1到100累加
func test01() int {
sum :=
for i :=;i<=;i++{
sum +=i
}
return sum
}
//递归实现1到100的累加
func test02(num int)int{
if num=={
return
}
return num +test02(num-)
} func main() {
fmt.Println(test01())
fmt.Println(test02())
}

2)注意:

() 有参无返回值 ,参数名就相当于函数体内层的局部变量,命名返回值变量会被初始化类型零值
()不支持默认值参数。
()不支持函数重载。
()不支持函数嵌套,严格地说是不支持命名函数的嵌套定义,但支持嵌套匿名函数

3 多值返回

func   swap(a,b int) (int,int){
return b,a
}

4 实参到形参的传递

package  main
import "fmt"
func chvalue(a int) int{
a=a+
return a
}
func chpointer(a *int){
*a = *a +
return
}
func main() {
a :=
chvalue(a) //实参传递给形参是值拷贝
fmt.Println(a) chpointer(&a) //实参传递给形参然仍是值拷贝,只不过复制的是a的地址值
fmt.Println(a)
}

5 不定参数

不定参数声明使用 (参数 .. . type) 的语法格式

特点:

() 所有的不定参数类型必须是相同的
()不定参数必须是函数的最后一个参数。
()不定参数名在函数体 内相当于切片,对切片的操作同样适合对不定参数的操作
package main
func sum(arr ...int)(sum int){
for _, v := range arr{ //arr相当于切片
sum += v
}
return
}

6 切片可以作为参数传递给不定参数,切片名后要加上 ”...“

package main

//import "go/types"

func sum(arr ...int)(sum int){
for _,v :=range arr{
sum += v
}
return
}
func main() {
slice := []int{,,,}
array := [...]int{,,,}
//数组不可以作为实参传递给不定参数的函数 所以sum(array...)会报错  cannot use array (type [4]int) as type []int in argument to sum
sum(slice...)
}

7  形参为不定参数的函数的函数和形参为切片的函数类型不相同

package main

import (
"fmt"
) func suma(arr ...int)(sum int){
for v := range arr{
sum += v
}
return
} func sumb(arr []int) (sum int) {
for v := range arr{
sum += v
}
return
}
func main() {
//suma 和sumb的类型不一样
fmt.Printf("%T\n",suma)
fmt.Printf("%T\n",sumb)
}

8  函数签名

概述:

函数类型又 函数签名 个函 类型就是函数定义首行去掉函数名、参数名和{,可以
使用台nt.Printf 的”%T”格式化参数打印函数的类型。
package main

import "fmt"

func add(a,b int) int {
return a + b
}
func main() {
fmt.Printf("%T\n",add) //打印效果 func(int, int) int
}

两个函数类型相同的条件是:拥有相同的形参列表和返回值列表(列表元素的次序、个数和类型相同)形参名可以不同

func add(a,b int) int { return a+b)
func sub (x int, y int) (c int) { c=x- y ; return c )

可以使用 type 定义函数类型,函数类型变量可以作为函数的参数或返

package main
import "fmt"
func add(a,b int) int {
return a + b
}
func sub(a,b int) int {
return a - b
}
type Op func(int,int) int //定义一个函数类型,输入的是两个int类型
//返回值是一个int类型
func do(f Op,a,b int) int { //定义一个函数,第一个参数是函数类型Op
return f(a,b) //函数类型变量可以直接用来进行函数调用
}
func main(){
a := do(add,,) //函数名add可以当作相同函数类型的形参
fmt.Println(a) //
s := do(sub,,)
fmt.Println(s) //-1
}

总结:

 实际函数类型变 和函数名都可以当作指针变量,该指针指向函数代码开始位置 通常说函数类型变量是一
种引用类型,未初始化的函数类型变量的默认值是nil
有名函数的函数名可以看作函数类型的常 ,可以
直接使用函数名调用函数,也可以直接赋值给函数类型变量,
package main

func sum(a,b int) int{
return a + b
}
func main(){
sum(,) //直接调用
f := sum //有名函数可以直接赋值给变脸
f(,)
}

9  匿名函数

Go 提供两种函数 有名函数和匿名函数。匿名函数可以看作函数字面量 所有直接使用函
数类型变量的地方都可以由匿名函数代替。医名函数可以直接赋值给函数变量,可以当作实参,
也可以作为返回值,还可以直接被调用
package main

import "fmt"

//匿名函数被直接赋值函数变量
var sum = func(a,b int) int {
return a + b
} func doinput(f func(int,int) int,a,b int) {
return
} //匿名函数作为返回值
func wrap(op string) func(int, int) int{
switch op {
case "add":
return func(a int, b int) int {
return a +b
}
case "sub":
return func(a int, b int) int {
return a + b
}
default:
return nil
}
}
func main() {
//匿名函数被直接调用
defer func() {
if err :=recover();err !=nil{
fmt.Println(err)
}
}()
sum(,)
//匿名函数作为实参
doinput(func(x , y int) int {
return x + y
},,)
opFunc :=wrap("add")
re := opFunc(,)
fmt.Printf("%d\n",re)
fmt.Println(f)
}

二 : defer关键字

使用
Go 函数里提供了 defe 关键字,可以注册多个延迟调用,这些调用以先进后出( FILO )的
顺序在函数返回前被执行
package main

import "fmt"

func test(x int){
fmt.Println(/x)
}
func main() {
//defer 是延迟操作
defer fmt.Println("aaa")
defer fmt.Println("bbb")
//报错并不影响程序的运行
defer test()
defer fmt.Println("ccc")
}

注意 :

  defer 后面必须是函数或方法的调用,不能是语句,否则会报
express on in defer must be function call 错误。
defer 函数的实参在注册时通过值拷贝传递进去。
package main
func f() int{
a :=
defer func(i int) {
println("defer i=",i) //defer i= 0
}(a)
a++
return a
}
func main() {
f()
}
//注:实参a 的值在defer注册时通过值拷贝传递进去,后续语句a++不会影响defer语句最后输出结果
 defer语句必须先注册后才能执行,如果defer位于return之后,则defer因为没有注册,不会执行
package main

func main(){
defer func() {
println("first")
}()
a := 0
println(a) //0
return defer func() {
println("second") //first
}()
}
 defer的好处是可以在一定程度上避免资源泄漏,特别是在有很多return语句,有多个资源需要关闭的场景,
很容易漏掉资源关闭操作
func CopyFile (dst , src string) (w int64 , err error) {
src , err := os.Open(src)
if err != nil {
return
}
dst, e rr := os . Create(dst)
if err != nil {
//src 很容易忘记关闭
src.Close ()
returη
}
w, err =工 Copy dst src )
dst.Close()
src.Close ()
return
}

  在打开资源无报错后直接调用 defer 关闭资源

func CopyFile (dst , src string) (w nt64 err error) {
src , err := os.Open (src )
if err != nil {
return
}
defer src . Close()
dst , err := os . Create(dst)
if err != nil {
return
}
defer dst. Close ()
w, err =工 Copy(dst src)
return
}
//总结 1defer 语句的位置不当,有可能导致 panic 一般 def1 语句放在错误检查语句之后。
//2defer 也有明显的副作用: defer 会推迟资源的释放, defer 尽量不要放到循环语句里面,将大函数内部的defer语句单独拆分成
//一个小函数是一种很好的实践方式。另外, defer 相对于普通的函数调用需要间接的数据结构的支持,相对于普通函数调用有一定的性能损耗
//3 defer r 中最好不要对有名返回值参数进行操作

四 :闭包

 概述:
闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿
名函数中引用外部函数的局部变量或包全局变构成。
引用:闭包=函数+引用环境
详解:
闭包对闭包外的环境引入是直接引用,编译器检测到闭包,会将闭包引用的外部变量分配到堆上
如果函数返回的闭包引用了该函数的局部变量( 参数或函数内部变量〉
()多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函
数都会为局部变量分配内存
()用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对
该外部变量都有影响,因为闭包函数共享外部引用。

示例:

package main

func fa(a int) func(i int) int{
return func(i int) int {
println(&a,a)
a= a+i
return a
}
}
func main(){
f :=fa() //f 引用的外部的闭包环境包括本次函数调用的形参a的值1
g := fa() //g 引用的外部的闭包环境包括本次函数调用的形参a的值1
//此时f、g引用的闭包环境中的a的值并不是同一个,而是两次函数调用产生的副本
println(f()) //0xc00006c000 1 2
//多次调用f引用的同一个副本a
println(f()) //0xc00006c000 2 3
//g中a的值然仍是1
println(g()) //0xc00006c008 1 2
println(f()) //0xc00006c000 3 4
}
//f和g引用的是不同的a
(3)如果函数返回的闭包引用的是全局变量 ,则多次调用该函数返回的多个闭包引用的都是同一个a。
同理,调用 个闭包多次引用的也是同一个 。此时如果闭包中修改了a 值的逻辑,
每次闭包调用都会影响全局变量 的值。

示例:

package main

var(
a=
) func fa() func(i int) int {
return func(i int) int {
println(&a,a)
a = a +i
return a
}
}
func main() {
f :=fa() //f 引用的外部的闭包环境包括全局交量a
g :=fa() //f 引用的外部的闭包环境包括全局变量a
///此时f、g 引用的闭包环境中的a 的值是同一个
println(f()) //0x4d68b8 0 1
println(g()) //0x4d68b8 0 2
println(g()) //0x4d68b8 0 3
println(g()) //0x4d68b8 0 4
}

  (4)同一个函数返回的多个闭包共享该函数的局部

package main

func fa(base int) (func(int) int,func(int) int) {
print(&base,base) //0xc00006c00000xc00006c00800xc00006c000 1
add := func(i int) int {
base += i
println(&base,base)
return base
}
sub := func(i int) int{
base -= i
println(&base,base)
return base
}
return add,sub
}
func main() {
//f、g 闭包引用的 base 是同一个,是fa函数调用传递过来的实参值
f,g := fa()
//s、k 包引用的base是同一个是fa函数调用传递过来的实参值
s,k := fa()
//f、g和s、k 引用不同的闭包交量,这是由于fa每次调用都妥重新分配形参
println(f())
println(g())
println(s())
println(k())
}

go语言之函数及闭包的更多相关文章

  1. swift1.2语言函数和闭包函数介绍

    swift1.2语言函数和闭包函数介绍 在编程中,随着处理问题的越来越复杂,代码量飞速增加.其中,大量的代码往往相互重复或者近似重复.如果不采有效方式加以解决,代码将很难维护. swift1.2语言函 ...

  2. 苹果新的编程语言 Swift 语言进阶(六)--函数和闭包

    一 .函数 1.1. 函数的定义和调用 函数的定义以funckeyword作为前缀,接着是函数名字,接着跟着一个能够带有參数.也能够不带參数的圆括号.接着用-> 指示函数的返回类型. 函数运行体 ...

  3. Swift 1.1语言第7章 函数和闭包

    Swift 1.1语言第7章  函数和闭包 在编程中,随着处理问题的越来越复杂.代码量飞速添加. 当中,大量的代码往往相互反复或者近似反复.假设不採有效方式加以解决.代码将非常难维护. 为了解决问题, ...

  4. 【Go语言学习】匿名函数与闭包

    前言 入坑 Go 语言已经大半年了,却没有写过一篇像样的技术文章,每次写一半就搁笔,然后就烂尾了. 几经思考,痛定思痛,决定金盆洗手,重新做人,哦不,重新开始写技术博文. 这段时间在研究Go语言闭包的 ...

  5. 《JS语言精粹》学习笔记 函数部分の闭包

    要理解闭包,首先要理解变量作用域,变量的作用域就两种,全局变量和局部变量,函数内部可以直接读取全局变量,函数外部无法读取函数内部的局部变量. 闭包定义:能读取函数内部局部变量的函数就是闭包,而只有函数 ...

  6. 如何设计一门语言(七)——闭包、lambda和interface

    人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只有在写编译器和虚拟机的时候才需要管什么是 ...

  7. 窥探Swift之函数与闭包的应用实例

    今天的博客算是比较基础的,还是那句话,基础这东西在什么时候都是最重要的.说到函数,只要是写过程序就肯定知道函数是怎么回事,今天就来讨论一下Swift中的函数的特性以及Swift中的闭包.今天的一些小实 ...

  8. 窥探 Swift 之 函数与闭包的应用实例

    今天的博客算是比较基础的,还是那句话,基础这东西在什么时候 都是最重要的.说到函数,只要是写过程序就肯定知道函数是怎么回事,今天就来讨论一下Swift中的函数的特性以及Swift中的闭包.今天的一些小 ...

  9. 函数:内嵌函数和闭包 - 零基础入门学习Python020

    函数:内嵌函数和闭包 让编程改变世界 Change the world by program 内嵌函数和闭包 接下来这两节课我们谈的话题可能会"比较高级",所以如果是零基础的朋友, ...

随机推荐

  1. 从jdbc到spring-boot-starter-jdbc

    从jdbc到spring-boot-starter-jdbc jdbc 是什么 JDBC是一种用于执行SQL语句的API,可以为多种关系数据库提供统一访问,它是由一组用Java语言编写的类和接口.是J ...

  2. 【oracle】-表连接类型:内连接,外连接...

    一.数据准备 1.emp表 delete from emp; insert into emp (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) ...

  3. What is 测试金字塔?

    我的女朋友是一名测试工程师,但她之前却不知道测试金字塔的概念,为此我曾经在家里的白板上画了一个图一层一层给她讲解过.我和同事在给团队面试测试和开发岗位时,也会必问到这个问题,想到可能有很多开发童鞋都不 ...

  4. 从SpringBoot源码分析 配置文件的加载原理和优先级

    本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级     跟入源码之前,先提一个问题:   SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...

  5. 【NOI2018】归程 题解(kruskal重构树+最短路)

    题目链接 题目大意:给定一张$n$个点$m$条边的无向图.每条边有长度和海拔.有$Q$次询问,每次给定起点$v$和当天水位线$p$,每次终点都是$1$.人可以选择坐车或走路,车只能在海拔大于水位线的路 ...

  6. MySQL面试题!新鲜出炉~

    01.Mysql 的存储引擎,myisam和innodb的区别? 答:1.MyISAM 是非事务的存储引擎,适合用于频繁查询的应用.表锁,不会出现死锁,适合小数据,小并发. 2.innodb是支持事务 ...

  7. 微信公众号添加zip文件下载

    微信公众号添加zip文件下载的教程 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件.如:zip ...

  8. Quartz.Net的基础使用方法,多任务执行继续扩展

    前一篇随笔讲了Quartz多任务的简单实现 Quartz.Net的基础使用方法,多任务执行 这一篇,来简单对前一篇进行一下简单的扩展 看了前一篇的代码会发现,每次新增一个任务还要去GetJobs方法里 ...

  9. LeetCode 122 best-time-to-buy-and-sell-stock-ii 详解

    题目描述 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你 ...

  10. 【luogu1613】跑路 - 倍增+Floyd

    题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟 ...