一:函数

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. Skill 脚本演示 ycAutoSnap.skl

    https://www.cnblogs.com/yeungchie/ ycAutoSnap.skl 版图编辑中自动吸附 Path 的 "垂直线头",也可以批量对齐 Bus 走线,也 ...

  2. Skill 计算两点距离

    https://www.cnblogs.com/yeungchie/ code procedure(ycHowFar(a b) prog((xAB yAB sAB) xAB = xCoord(a) - ...

  3. 一个Java方法能使用多少个参数?

    我最近给我fork的项目QuickTheories增加了一个接口: @FunctionalInterface public interface QuadFunction<A, B, C, D, ...

  4. 如何利用Python在网上接单,兼职也能月薪过万

  5. SpringBoot介绍,快速入门小例子,目录结构,不同的启动方式,SpringBoot常用注解

    SpringBoot介绍 引言 为了使用ssm框架去开发,准备ssm框架的模板配置 为了Spring整合第三方框架,单独的去编写xml文件 导致ssm项目后期xml文件特别多,维护xml文件的成本也是 ...

  6. 解决Android v4、v7包导入标红问题import android.support.v4.app.ActivityCompat;import android.support.v7.app

    如果有如下错误:import android.support.v4.app.ActivityCompat;import android.support.v7.app.AppCompatActivity ...

  7. Css 设置超过再两行显示省略号

    大部分场景都是超过一行就显示... <template> <div class="other-product-item item-name" :title=&qu ...

  8. CTFhub-WEB前置-http协议闯关

    前情提要: 在渗透学习过程中,web的基础知识很重要,在这里通过long long ago之前学习的http基础,并结合网上的CTFhub--WEB前置之http协议闯关,对web基础知识进行加固并查 ...

  9. QUIC协议详解之Initial包的处理

    从服务器发起请求开始追踪,细说数据包在 QUIC 协议中经历的每一步.大量实例代码展示,简明易懂了解 QUIC. 前言 本文介绍了在 QUIC 服务器在收到 QUIC 客户端发起的第一个 UDP 请求 ...

  10. Android 本地缓存Acache的简单使用

    设置缓存数据: ACache mCache = ACache.get(this); mCache.put("key1", "value"); //保存6秒,如果 ...