热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang学习笔记-func函数

函数function    -Go函数不支持嵌套、重载和默认参数    -但支持以下特性:    &nbs

函数function

    - Go函数不支持 嵌套、重载和默认参数

    - 但支持以下特性:

        无需声明原形、不定长变参、多返回值、命令返回值参数、匿名函数、闭包

    - 定义函数使用关键字func,且大括号不能另起一行(所有有大括号的均遵循此原则)

    - 函数也可以作为一种类型的使用,直接赋值给变量(匿名函数)

定义一个函数

    格式:func name( 传入的变量1 类型,变量2 类型 ) [ 返回变量 类型,变量 类型 ]{ }

    - 传入的变量可以没有,也可以使多个

    - 当传入的变量类型相同时,可以全部省略只留最后一个

        func a(a,b,c int) {}

    - 返回值可以有多个,返回值类型相同,也可以只留最后一个,其中返回变量名称可以省略,省略的话,就需要每返回一个写一个变量的类型了,如果指定了返回某个局部变量,那么这个变量就已经被定义,那么在函数体内即可直接使用。

    - 不指定返回变量名称,那么需要在函数尾部写入 return 变量1,变量2, 如果指定了返回的变量名,那么只需要写上return即可。

    - 传入的参数个数,也可以不定(不定长变参),使用...来表示,在函数体内存储这些数据的类型为slice

    func A(a ...int)  -->...int必须放在最后

    - 如果传入的值有1个string,有n个int,那么只能 fun A(b string, a ...int)这种形式接受

    - 如果传入的参数是一个常规的int、string等类型的话,属于值传递(默认),即只是值得拷贝,而如果传递sllice,则是引用传递(其实slice也属于值拷贝,只不过,slice拷贝的是内存地址。而直接修改内存地址会影响源数据)

    - 如果需要把int、string类型的值传入并修改,那么就需要把这些类型的变量的内存地址传入

package main
import "fmt"
func main() {
    a := 2
    A(a)
    fmt.Println(a)
}
func A(a int) {
    i := 3
    fmt.Println(i)
}
结果:
3
2


把变量a的地址传入到函数中

package main
import "fmt"
func main() {
    a := 2
    A(&a)    //&a表示取a的内存地址
    fmt.Println(a)
}
func A(a *int) {    //定义指针类型,指向a的内存地址
    *a = 3    //直接对内存地址进行赋值
    fmt.Println(*a)
}
结果:
3
3


参数传递(传值与传指针)

    函数的参数传递分为两种,值传递,和引用传递,值传递指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,GO是值传递,在调用过程中不会影响到实际参数。

    变量在内存中是存放于一定的地址上的,修改变量实际是修改变量地址处的内存。只有让局部函数知道参数的内存地址,才能修改变量的值。所以引用传递的时候需要把变量的内存地址传入到局部函数内(&a,&表示传递变量的内存地址),并将函数的参数类型调整为*int,即改为指针类型,才能在函数中修改变量的值,此时参数仍然是copy传递的,只不过copy的是一个指针。


函数作为其他变量的值

    在Go语言中,一切皆类型,函数也可以被命名为变量,然后对变量进行函数的调用

package main
import "fmt"
func main() {
    a := A
    a()
}
func A() {
    fmt.Println("Func A")
}
结果:
Func A


匿名函数

    在定义函数的时候不指定函数的名称,而是把函数直接赋值给某个变量的函数叫做匿名函数,调用这个函数的时候,直接使用变量的名称即可。(因为golang中的func不支持函数嵌套,使用匿名函数可以达到嵌套的效果)   匿名函数不能作为顶级函数(最外层)

package main
import "fmt"
func main() {
    a := func() {
    fmt.Println("Func")
}
    a()
}


闭包函数

    所谓闭包函数就是将整个函数的定义一气呵成写好并赋值给一个变量。然后用这个变量名作为函数名去调用函数体。闭包函数对它外层的函数中的变量具有访问和修改的权限

package main
import "fmt"
func main() {
    f := closure(10)    //调用闭包函数并传递10
    fmt.Println(f(1))    //传递1给返回的函数,10+1=11
    fmt.Println(f(2))    //传递2给返回的函数,10+2=12
}
func closure(x int) func(int) int {   //定义一个函数接收一个参数x,返回值也是一个函数接收一个变量y
    return func(y int) int {    //返回一个int,函数接收一个参数,返回x+y的值
        return x + y
    }
}
结果:
11
12


defer

    - 执行方式类似其他语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行 (类似于栈的方式,先进后出,后进先出)

    - 即使函数发生了严重错误也会执行

    - 支持匿名函数的调用

    - 常用语资源清理、文件关闭、解锁以及记录时间等操作

    - 通过与匿名函数配合可在return之后修改函数计算结果

    - 如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时已经获得了拷贝,否则则是引用某个变量的地址

    - Go没有异常机制,但有panic/recover模式来处理错误

    - Panic可以再任何地方引发,但recover只有在defer调用的函数中有效

例子

package main
import "fmt"
func main() {
    fmt.Println("a")
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
}
结果:
a
3
2
1
可以看到,在程序执行完毕后,defer是从最后一条语句开始执行的,证明了defer类似栈的运行方式


defer搭配循环的结果

package main
import "fmt"
func main() {
    for i := 0; i < 3; i++ {
    defer fmt.Println(i)
    }
}
结果:
2
1
0


panic/recover实例

主要用来对程序的控制,并且仅针对函数级别的错误进行收集与回调。使程序能继续运行下去

package main
import "fmt"
func main() {
    A()
    B()
    C()
}
func A() {
    fmt.Println("A")
}
func B() {
    defer func() {    //这里定义defer执行一个匿名函数,用于捕捉panic,这里如果把defer放在panic之后那么程序执行到panic后就会崩溃,那么defer就不会生效
        if err := recover(); err != nil {    //对引发的panic进行判断,由于手动触发了panic并发送了信息,那么用recover接收的异常返回值就要不为空,如果为nil表示没有异常,不为nil就表示异常了,这里对recover的返回值进行判断
    }
}()
    panic("this is painc")//发送异常,异常信息为”this is panic“
}
func C() {
    fmt.Println("C")
}
结果:
A
C
由于在函数B中定义了异常的recover机制,所以不会迫使程序退出,会继续执行

panic/recover 实例2

package main
import "fmt"
func main() {
    fmt.Println("1")
    fmt.Println("2")
    f := func() {
        defer func() {
            if err := recover(); err != nil {
            fmt.Println("panic")
            }
        }()
    panic("hello world")
    fmt.Println("7")
    }

    f()
    fmt.Println("8")
    }
结果:
1
2
panic      //打印panic说明程序已经成功的捕捉到了异常
8


    定义了匿名函数,并赋值给了变量f,匿名函数中的"7"不会打印,因为执行到panic已经崩溃了,而我们在匿名函数内定义了recover捕捉,所以匿名函数会被退出,然后继续执行其他程序


扩展:

    在go语言中是没有异常捕获机制的,通过panic/recover来实现错误的捕获以及处理,利用go函数多返回值的概念,来进行check,如果err等于nil表示没有发生错误,当程序发生比较严重的错误,严重到无法弥补,比如索引越界,由于我们不能准确的判断元素的个数,所以recover也没有意义,所以说这个时候就是一个panic。如果知道可能会索引越界,并且希望程序能从错误中回复回来,那么这时候就需要用到recover,一旦调用recover,系统就会认为你需要从panic状态恢复过来,当程序进入panic状态,那么正常的程序将不会被执行,那么需要定义defer来执行recover(),defer不管在任何状态下,都会执行,只要把recover放在defer中,那么不管程序发生了怎样的错误,程序都会回复过来,需要注意的是defer类似栈的模式,后进先出。在可能发生panic的程序之前,预先定义defer,否则程序运行到painc后直接崩溃了,这个时候他只会去检查预先定义好的defer,而你放在panic之后,将会失效


例子1:

判断奇偶数

package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
    fmt.Println("the slice is ", a)
    fmt.Println("the odd is ", odd(a))
    fmt.Println("the even is ", even(a))
}
func odd(num []int) []int {
    var result []int
        for _, value := range num {
            if value%2 == 0 {
                result = append(result, value)
            }
        }
    return result
}
func even(num []int) []int {
    var result []int
        for _, value := range num {
            if value%2 == 0 {
                continue
        }
    result = append(result, value)
    }
return result
}


思路:分别对切片进行过滤,偶数功能模块过滤一遍,挑出偶数,奇数功能模块过滤一遍,挑出奇数。缺点,模块复用 性差。


判断奇偶数:

package main
import (
    "fmt"
)
type funcation func(int) bool
func odd(num int) bool {
    if num%2 == 0 {
        return false
    }
    return true
}
func even(num int) bool {
    if num%2 == 0 {
        return true
        }
    return false
}
func filter(slice []int, f funcation) []int {
    var result []int
        for _, value := range slice {
            if f(value) {
                result = append(result, value)
            }
        }
    return result
}
func main() {
    a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("the slice is ", a)
    fmt.Println("the odd is ", filter(a, odd))
    fmt.Println("the even is ", filter(a, even))
}


思路:把判断奇偶的功能模块化,然后再通过一个模块调奇偶判断模块,然后再用main函数组织,(使用func类型,进行功能模块的传递),有点,结构性强,逻辑强。



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • OO第一单元自白:简单多项式导函数的设计与bug分析
    本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • EPPlus绘制刻度线的方法及示例代码
    本文介绍了使用EPPlus绘制刻度线的方法,并提供了示例代码。通过ExcelPackage类和List对象,可以实现在Excel中绘制刻度线的功能。具体的方法和示例代码在文章中进行了详细的介绍和演示。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
author-avatar
antingwolf
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有