文章目录

  • 一. 函数定义
    • 1.1 函数的定义
    • 1.2 调用方式:
    • 1.3 案例
  • 二.函数参数说明
    • 2.1 类型的简写
    • 位置参数
    • 2.2 可变参数
    • 2.3 多返回值
      • 2.3.1 正常返回值
      • 2.3.2 无返回值
      • 2.3.3 多个返回值
    • 2.4 返回值命名
  • 三.函数的作用域
    • 3.1 局部变量
    • 3.2 全局变量
  • 四. 匿名函数和闭包
    • 4.1 匿名函数
    • 4.2 闭包
  • 五. 指针类型
  • 六.值传递
    • 6.1 值拷贝
    • 6.2 函数传参
      • 6.2.1 案例1
      • 6.2.2 案例二
    • 6.2.3 案例三
  • 七.高阶函数
    • 7.1 把函数作为参数
      • 7.1.1 案例一
      • 7.1.2 案例二
    • 7.2 以函数作为返回值
  • 八. 总结

一. 函数定义

为什么要使用函数,因为有时候相同的功能需要重复写:
每个数值相加都得需要重新写一遍

使用函数就可以减少代码的重复,提高代码的复用性,减少代码冗余,代码维护性也提高了。

1.1 函数的定义

函数就是:为完成某个功能的程序命令结合
go语言是通过func关键字声明一个函数的,声明语法格式如下

func 函数名(形式参数) (返回值) {函数体return 返回值   // 函数终止语句
}
func intSum(x, y int) int {return x + y
}

上面的代码中,intSum函数有两个参数,这两个参数的类型均为int,因此可以省略x的类型,因为y后面有类型说明,x参数也是该类型。

  • 函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
  • 形式参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
  • 返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。
  • 函数体:实现指定功能的代码块。

1.2 调用方式:

函数名()

1.3 案例

求 1+2+3+…+100的值日

package mainimport "fmt"func sum() {var s = 0for i := 1; i <= 100; i++ {s = s + i}fmt.Println(s)
}
func main() {sum()}

二.函数参数说明

为什么需要参数?
形参和实参是一一对应的

2.1 类型的简写

位置参数

函数的参数中如果相邻变量的类型相同,则可以省略类型,例如

package mainimport "fmt"func add(x, y int) {  //位置参数,也叫形式参数
//此处x int ,y int的写法可以缩写为 x.y intfmt.Println(x + y)
}
func main() {add(2, 5)//2,5是实际参数,有几个实际参数就有几个形式参数,一一对应}

x,y的值是一一对应的,不能出现x传递到y上去的情况

练习:
计算从1到100的值,但是希望可以通过输入两个实际参数来计算,比如计算5到100, 3到1000等等。

import "fmt"func sum(x, y int) {var t = 0for i := x - 1; i <= y; i++ {t = t + i}fmt.Println(t)
}
func main() {sum(1, 100)sum(2,150)}

2.2 可变参数

可变参数也叫不定长参数
举例:

package mainimport ("fmt""reflect"
)func add(nums ...int) {fmt.Println(nums, reflect.TypeOf(nums))
}
func main() {add(1, 2, 3)add(1, 2)}

执行结果:

可以看到: 无论传几个参数,都是切片类型

我们可以做它的累加和:

package mainimport ("fmt"
)func add(nums ...int) {//fmt.Println(nums, reflect.TypeOf(nums))t := 0for _, v := range nums {//fmt.Println(i, v)t += v}fmt.Println(t)
}
func main() {add(1, 2, 3)add(1, 2)}

注意: 如果有位置参数先传递位置参数,等没有位置参数了之后再传递不定长参数

例如:

源码如下:

package mainimport ("fmt""reflect"
)func add(author string, nums ...int) {fmt.Println(author, nums, reflect.TypeOf(nums))t := 0for _, v := range nums {//fmt.Println(i, v)t += v}fmt.Println(t)
}
func main() {add("laoxin", 1, 2, 3)add("laowang", 1, 2)}

2.3 多返回值

2.3.1 正常返回值

Go语言中函数支持多返回值,函数如果有多个返回值时必须用()将所有返回值包裹起来

语法:

func calc(x, y int) (int, int) {sum := x + ysub := x - yreturn sum, sub
}

文字版:

func 函数名(形式参数1,形式参数2,...)返回值类型{函数体
return 值
}
调用函数
函数名(实际参数)

一旦有返回值,就必须要写值的类型

举例:

在不需要返回类型的时候,我们可以在终端打印

当我们要用到return 返回的时候,需要加返回值类型:


这样与终端打印有什么区别呢?

有区别,返回值还可以调用

package mainimport "fmt"func add(a, b int) int {//fmt.Println(a + b)return a + b
}
func mul(x, y int) int {//fmt.Println(a + b)return x * y
}func main() {ret1 := add(1, 2)  //这个值是加法最后的结果赋值给了ret1//fmt.Println(ret1)ret2 := mul(ret1, 100) //ret1又作为参数x传递给另外一个函数mulfmt.Println(ret2)}

2.3.2 无返回值

把结果作为值返回,再次作为参数使用,只有需要打印的时候才会打印

注意,无返回值的不能进行运算,只完成一个调用的功能
func foo(){
fmt.println(“ok”)
}

foo() // foo函数无返回值则不可以将调用出进行复制运算

2.3.3 多个返回值

package mainimport "fmt"func get_name_age() (string, int) {return "laoxin", 40}func main() {name, age := get_name_age()fmt.Println(name, age)
}

2.4 返回值命名

这个知识点,面试题的常客

前面的例子中,都没有做返回值命名

而所谓的返回值命名是指的

函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回

package mainimport "fmt"func get_name_age() (name string, age int) {name = "laoxin"age = 10return}func main() {name, age := get_name_age()fmt.Println(name, age)
}

如果我们在函数内不定义name和age,会报错吗?

不会

package mainimport "fmt"func get_name_age() (name string, age int) {return}func main() {name, age := get_name_age()fmt.Println(name, age) //得到空和0
}

可以看到得到的结果是空字符串和0, 因为string和int都是值类型,在不赋值的时候有默认值

三.函数的作用域

3.1 局部变量

一说作用域指的是变量作用域
所谓变量作用域,即变量可以作用的范围。

作用域(scope)通常来说,程序中的标识符并不是在任何位置都是有效可用的,而限定这个标识符的可用性的范围就是这个名字的作用域。

变量根据所在位置的不同可以划分为全局变量和局部变量

局部变量 :写在{}中或者函数中或者函数的形参, 都是局部变量.

package mainimport "fmt"var x = 100 //全局作用域func foo() {var x = 10 //局部作用域,局部有X,就执行局部的,局部没有去拿全局的fmt.Println(x)}
func bar() {var x = 20fmt.Println(x)}func main() {foo()}

如果在main函数里也加个println输出的结果

package mainimport "fmt"var x = 100 //全局作用域 func foo() {var x = 10 //局部作用域,局部有X,就执行局部的,局部没有去拿全局的fmt.Println("foo函数的x值日", x)// 结果是10}
func bar() {var x = 20fmt.Println(x)}func main() {foo()fmt.Println("main的函数x", x) //结果为100}

如果局部变量不声明会出现什么情况

package mainimport "fmt"var x = 100 //全局作用域func foo() {x = 10 // 重新复制,x在foo里面没有,于是去全局拿了个x,但赋值为10,用的是全局的xfmt.Println("foo函数的x值日", x, &x)  // 0xa9a258 可以看到内存地址和全局的x地址一样}
func bar() {var x = 20fmt.Println(x)}func main() {foo()fmt.Println("main的函数x", x, &x) // 0xa9a258}

注意:

1.、局部变量的作用域是从定义的那一行开始, 直到遇到 } 结束或者遇到return为止2、局部变量, 只有执行了才会分配存储空间, 只要离开作用域就会自动释放3、局部变量存储在栈区4、局部变量如果没有使用, 编译会报错。全局变量如果没有使用, 编译不会报错5、:=只能用于局部变量, 不能用于全局变量
6. 全局变量定义的越少越好

3.2 全局变量

全局变量 :函数外面的就是全局变量

1、全局变量的作用域是从定义的那一行开始, 直到文件末尾为止2、全局变量, 只要程序一启动就会分配存储空间, 只有程序关闭才会释放存储空间,3、全局变量存储在静态区(数据区)
func foo()  {// var x =10x = 10fmt.Println(x)
}var x = 30   // 全局变量func main() {`在这里插入代码片`// var x = 20foo()fmt.Println(x)

四. 匿名函数和闭包

4.1 匿名函数

函数当然还可以作为返回值,但是在Go语言中函数内部不能再像之前那样定义函数了,只能定义匿名函数。匿名函数就是没有函数名的函数,匿名函数多用于实现回调函数和闭包。 匿名函数的定义格式如下:

   func(参数)(返回值){函数体
}

匿名函数没有名字,但可以复制给其他变量

package mainimport "fmt"func main() {add := func(x, y int) {fmt.Println(x + y)}add(10, 20)
}

结果为30

匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:

作为立即执行函数的写法:

package mainimport "fmt"func main() {//add := func(x, y int) {////    fmt.Println(x + y)//}//add(10, 20)func(a,b int){fmt.Println(a +b)}(10,30)  //在这个位置直接写参数
}

4.2 闭包

闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量。

说一下我自己对闭包的理解,闭包就是函数返回一个匿名函数。简单的说:函数 + 引用环境 = 闭包

闭包就是一个函数和其他相关的引用环境组合的一个整体


案例一:

package mainimport "fmt"func returnNum() func() (int, int)  {return func() (int, int) {return 0, 1}
}
func main()  {q := returnNum()a, b := q()fmt.Println(a, b) //0, 1
}

案例二: 累加器

package mainimport "fmt"//写一个累加器
func AddUpper() func(int) int {var n int = 10return func(x int) int {n = n + xreturn n}
}
func main() {//使用前面的代码f := AddUpper()fmt.Println(f(1))fmt.Println(f(2))fmt.Println(f(3))
}

对上面的代码总结:

  • AddUpper是一个函数,返回的数据类型是fun(int)int
  • 闭包部分:

    返回的是一个匿名函数,但这个匿名函数引用到了函数外的n,因此这个匿名函数和n就形成了一个整体,构成了闭包。
  1. 闭包是一个类(),函数是操作,n是字段 ,函数和他使用到的n构成闭包
    4)当我们反复的调用f函数时,因为n是初始化一次,因此没调用一次就进行更新和累加
    5) 我们要搞清楚闭包的关键,就是要分析出返回的函数,它引用到哪些变量,函数和它引用到的变量共同构成了闭包

感受:匿名函数中引用的那个变量会一直保存在内存中,可以一直使用

五. 指针类型

新增一个数据类型叫指针类型

对于我们以前学的int类型,在同一个函数内,他的内存地址是一样的吗?


答案是一样的。

但问题是: x是什么样的变量?
在这里x是整型变量, x存在的是整型

var s =“hello” 这时候s是一个字符串变量

package mainimport ("fmt""reflect"
)func main() {//声明赋值一个整型变量var x = 100fmt.Println(&x)  //这个地址可不可以赋值给变量var p = &x //这个p就是指针变量// var p *int  fmt.Println(p,reflect.TypeOf(p))  //*int  和整型相关的指针变量类型}

指针类型 *int *string

存地址的变量就是指针变量

使用指针类型取值:

package mainimport ("fmt""reflect"
)func main() {//声明赋值一个整型变量var x = 100fmt.Println(&x) //这个地址可不可以赋值给变量var p = &x      //这个p就是指针变量// var p *intfmt.Println(p, reflect.TypeOf(p)) //*int  和整型相关的指针变量类型fmt.Println(*p) // * 是取值, p是内存地址。 *p就是获取p内存地址的值,地址对应的值取出来
}

fmt.Println(*p) // * 是取值, p是内存地址。 *p就是获取p内存地址的值,地址对应的值取出来

六.值传递

6.1 值拷贝

package mainimport "fmt"func main() {var x = 10fmt.Printf("x的地址%p\n", &x)y := x //值拷贝fmt.Printf("y的地址%p\n", &y)fmt.Println(y)
}


1) x和y地址不同
2) 如果你给Y赋值,x的值不会变

切片的值拷贝会怎么样呢?

结论:

  1. a的地址和b的地址不同
  2. a[0]的值变了后,b的也会变

6.2 函数传参

6.2.1 案例1

package mainimport "fmt"func func01(a int) {//fmt.Println(x)a = 100
}func main() {//// 案例1var x = 10func01(x) // a = x ,值拷贝fmt.Println(x)}

结论:
1) 不管func01函数给a赋值为多少,都不影响main函数的x值等于10

6.2.2 案例二

案例一中a=100,最终想要获取到,怎么获取

package mainimport ("fmt""reflect"
)func func02(a *int) {fmt.Println(a)                      // x的地址fmt.Println(*a, reflect.TypeOf(*a)) //*a = 100
}func main() {// 案例2var x = 10var p *int = &xfmt.Println(p)func02(p) // a = pfmt.Println(":::", x)}

6.2.3 案例三

如果是切片类型呢

package mainimport ("fmt"
)func func03(s []int) {fmt.Printf("func02的s的地址:%p\n", &s)// s[0] = 100s = append(s, 1000)
}
func main() {// 案例3var s = []int{1, 2, 3}fmt.Printf("main的s的地址:%p\n", &s)func03(s)fmt.Println(s)}

七.高阶函数

一个高阶函数应该具备下面至少一个特点:

  • 将一个或者多个函数作为形参
  • 返回一个函数作为其结果

首先明确一件事情:函数名亦是一个变量

时间操作:

打印时间戳:

package mainimport ("fmt""time"
)func main() {fmt.Println(time.Now().Unix())  //打印时间戳time.Sleep(time.Second * 2)  //停2秒钟fmt.Println(time.Now().Unix())
}

两种情况是高级函数,一种是把函数作为参数,一种是把函数作为返回值

7.1 把函数作为参数

7.1.1 案例一

package mainimport ("fmt""time"
)func bar() {time.Sleep(time.Second * 3)fmt.Println("bar")
}func foo() {time.Sleep(time.Second * 2)fmt.Println("foo")
}func funcTimer(f func()) {t1 := time.Now().Unix()//bar()//foo()f()t2 := time.Now().Unix()fmt.Println("spend time:", t2-t1)}func main() {// (1) 以函数为参数funcTimer(foo)funcTimer(bar)}

funcTimer 以函数作为参数,所以他就是高阶函数

7.1.2 案例二

双值计算器

package mainimport "fmt"func add(x, y int) int {return x + y
}func mul(x, y int) int {return x * y
}func caa(x, y int) int {return x - y
}func cal(a, b int, calFunc func(int, int) int) { //3个形参,2个int的,一个函数的ret := calFunc(a, b)fmt.Println(ret)}
func main() {cal(10, 5, mul)cal(111, 20, add)}

7.2 以函数作为返回值

package mainimport "fmt"func foo() func(int, int) string {// 方式1/*var bar = func(x, y int) string {fmt.Println("bar...")return "bar"}return bar*/// 方式2return func(x, y int) string {fmt.Println("bar...")return "bar"}}func main() {var ret = foo()ret(1, 2)}

八. 总结

函数这部分内容还是比较重要的,学习起来东西也比较繁琐,接下来,我们要进入复习阶段,利用现在学的一起做做练习吧。

go语言基础-函数应用相关推荐

  1. ostream作为函数返回值_GO语言基础函数

    GO语言基础–函数 函数是组织好的.可重复使用的.用于执行指定任务的代码块.本文介绍了Go语言中函数的相关内容. 函数定义 Go语言中定义函数使用func关键字,具体格式如下: func 函数名(参数 ...

  2. python语言基础实验_实验二Python语言基础函数包练习.doc

    实验二Python语言基础函数包练习 实验 Python语言基础函数包练习:1208 学号: 实验目的 1.Python语言包,如math.NumPySciPy和Matplotlib等函数包的使用实验 ...

  3. C语言基础-函数的概念

    c语言基础-函数的概念 一.函数的定义: 将常用的整体实现某个功能的代码块封装起来,用到的时候可以直接调用 函数也是模块化编程的一种体现 二.函数的定义格式: 函数类型 函数名(形参类型 形参名,形参 ...

  4. [C/C++语言基础] —函数

    目录 1.函数是什么 2.库函数 3.自定义函数 4.函数参数 5.函数的调用 6.函数的嵌套调用和链式访问 7.函数的声明和定义 8.函数递归 1.函数是什么 函数:有输入参数和返回值 返回类型  ...

  5. R语言基础函数assign函数

    R语言通过assign函数给变量赋值.生成新的变量.数据对象 assign("t",1.414) t #此命令等于: t <- 1.414;t 通过assign函数给变量赋值 ...

  6. C++语言基础 —— 函数

    [函数] 求正整数2和n之间的完全数(信息学奥赛一本通-T1150):点击这里 素数个数(信息学奥赛一本通-T1151):点击这里 最大数max(x,y,z)(信息学奥赛一本通-T1152):点击这里 ...

  7. C语言基础函数(二)

    2 函数的调用 2.1 函数的简单调用 Turbo C2.0调用函数时直接使用函数名和实参的方法,也就是将要赋给被调用函数的参量,按该函数说明的参数形式传递过去,然后进入子函数运行,运行结束后再按 子 ...

  8. fig r函数_R语言基础绘图函数散点图~跟着Nature Communications学画图~Figure1

    今天继续 跟着Nature Communications学画图系列第二篇.学习R语言基础绘图函数画散点图. 对应的 Nature Communications 的论文是 Fecal pollution ...

  9. C语言基础:数组作为函数参数传递演示源码

    将做工程过程中常用的内容片段记录起来,如下内容内容是关于C语言基础:数组作为函数参数传递演示的内容,应该能对小伙伴也有好处. #include <stdio.h>void show_arr ...

最新文章

  1. dojo 的DOM操作 dojo/dom
  2. C# 8.0 默认接口实现
  3. 国内各大平台的推荐算法,看到360的时候笑喷了……
  4. python递归算法案例教案_python教案
  5. POS打印机系列之 = 并口
  6. Jurassic.ScriptEngine 简易示例
  7. 很详细的“追女生技巧”
  8. centos上升级node_在centos7安装nodejs并升级nodejs到最新版本
  9. SQL Server商业智能功能–创建简单的OLAP多维数据集
  10. java 找到一行 更换单词_Java实现对一行英文进行单词提取功能示例
  11. python工资一般多少西安-西安Python和人工智能的薪资前景到底怎么样?
  12. extern 头文件 定义声明
  13. 交易者的量化程序化交易之路
  14. 第五节 B-S看涨看跌期权定价
  15. ERR invalid expire time in setex
  16. 使用Python将MNIST数据集手写数字转化为图片
  17. WORD设置从开始页数算总页数
  18. Java第八次实验要求
  19. QRCode生成二维码和解析二维码
  20. HTML+CSS静态网页作业:NBA勒布朗詹姆斯篮球明星带js(5页)

热门文章

  1. Elasticsearch从入门到放弃:文档CRUD要牢记
  2. 单片机流水灯串口控制流水灯
  3. UVALive - 3353 Optimal Bus Route Design(二分图最佳匹配)
  4. [Android测试] AS+Appium+Java+Win 自动化测试之九:PO模式的实例与ReportNg测试报告
  5. MURF1640CT-ASEMI高耐压快恢复二极管16A 400V
  6. 开源电路仿真软件circuitjs1使用时连线问题--坏的连接
  7. 请更换备份电池 pos机_请认真检查你的POS机费率,不要再花冤枉钱
  8. java 不能使用foreach,foreach不适用于表达类型
  9. 小学创客课程教学设计
  10. keytool制作CA根证书以及颁发二级证书