一:函数

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())
}

最新文章

  1. Mybatis实现数据的增删改查(CRUD)
  2. 设计模式之美:Memento(备忘录)
  3. ubuntu13.10 登陆后黑屏,没有菜单栏,可以启动termina,怎么解决?
  4. Struts2单文件上传
  5. Redis用户添加、分页、登录、注册、加关注案例
  6. SPOJ 687 Repeats(后缀数组+ST表)
  7. ASP.NET 母版页和内容页的加载顺序
  8. 手机自动化测试:appium源码分析之bootstrap十二
  9. 四、I/O
  10. 如何快速部署 Prometheus?- 每天5分钟玩转 Docker 容器技术(85)
  11. panic: interface conversion: interface {} is nil, not chan *sarama.ProducerError
  12. IP通信基础课堂笔记----关于数链层
  13. python--编写用例脚本
  14. HDFS的一些配置文件修改
  15. mysql的某个数据库拒绝访问的问题
  16. sql 把多列内容合并
  17. Spring Boot入门第三天:配置日志系统和Druid数据库连接池。
  18. points from ZhiQIng Hu
  19. java-继承的注意事项
  20. bzoj千题计划254:bzoj2286: [Sdoi2011]消耗战

热门文章

  1. ZROI 提高十连测 Day1
  2. 畅购商城(五):Elasticsearch实现商品搜索
  3. 【python设计模式-创建型】工厂方法模式
  4. ios企业签名为什么会掉签?
  5. python1.1列表知识点:
  6. 关于Exceptionless日志收集框架如何关闭磁盘缓存
  7. 001_HyperLedger Fabric环境安装
  8. APP常用控件学习理解
  9. JS 节点笔记
  10. springboot多环境配置文件