golang学习笔记-func函数

函数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类型,进行功能模块的传递),有点,结构性强,逻辑强。

时间: 2024-07-28 16:34:30

golang学习笔记-func函数的相关文章

python学习笔记之函数总结--高阶函数以及装饰器

python学习笔记之函数总结--高阶函数以及装饰器 Python特点: 1.不是纯函数式编程(允许变量存在): 2.支持高阶函数(可以传入函数作为变量): 3.支持闭包(可以返回函数): 4.有限度的支持匿名函数: 高阶函数: 1.变量可以指向函数: 2.函数的参数可以接收变量: 3.一个函数可以接收另一个函数作为参数: 下面我将示例一些函数的写法以及使用,并说明python中函数的特性: 1.基本的高阶函数示例: #!/usr/bin/env python def func():      

MySQL学习笔记-自定义函数

MySQL学习笔记-自定义函数 1.自定义函数简介 自定义函数:用户自定义函数(user-defined function,UDF)是一种对MySQL扩展的途径,其用法与内置函数相同 自定义函数的两个必要条件:(1)参数  (2)返回值 自定义函数: 创建自定义函数 CREATE FUNCTION function_name RETURNS {STRING|INTEGER|REAL|DECIMAL} routine_body 关于函数体: 1.函数体可以由合法的SQL语句构成: 2.函数体可以是

C和指针 学习笔记-4.函数

参数传递: 参数传递采用按值传递 ADT&黑盒 ADT:abstract data type,抽象数据类型 c可以用于设计与实现抽象数据类型,因为它可以限制函数和数据定义的作用域,这种技巧也称为黑盒设计 user.h #define MAXLEN 3 struct UserClz { char *name; char *phone; char *address; }; typedef struct UserClz User; /* *接函数 *通地名称查找地址 */ char const * l

day8_python学习笔记_chapter11_函数

1. 返回对象的数目   python实际返回的对象 0 -> None ; 1 -> object ; >1 -> tuple 2. 内部/内嵌函数:如果内部函数的定义包含了再外部函数里定义的对象的引用, 内部函数会变成被称为闭包的特别之物. 3. 装饰器 day8_python学习笔记_chapter11_函数,布布扣,bubuko.com

lua学习笔记之函数

Lua学习笔记之函数 1.  函数的作用 函数主要完成指定的任务,这样的情况下函数作为调用语句使用,函数可以计算并返回值,这样的情况下函数作为赋值语句的表达式使用. 语法: funcationfunc_name(arguments-list) Statements-list end 调用函数的时候,如果参数列表为空,必须使用()表示是函数调用. Print(8*9,9/8) a = math.sin(3) +math.cos(10) print(os.date()) 上述规则有一个例外,当函数只

matlab学习笔记 bsxfun函数

matlab学习笔记 bsxfun函数 最近总是遇到 bsxfun这个函数,前几次因为无关紧要只是大概看了一下函数体去对比结果,今天再一次遇见了这个函数,想想还是有必要掌握的,遂查了些资料总结如下.   函数bsxfun [功能描述]两个数组间元素逐个计算. [应用场合]当我们想对一个矩阵A的每一列或者每一行与同一个长度相等的向量a进行某些操作(比较大小,乘除等)时,我们只能用循环方法或者利用repmat函数将要操作的向量a复制成和A一样尺寸的矩阵,进而进行操作.从MATLAB R2007a开始

MySQL学习笔记—自定义函数

MySQL学习笔记-自定义函数 注释语法: MySQL服务器支持3种注释风格: 从'#'字符从行尾. 从'– '序列到行尾.请注意'– '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如空格.tab.换行符等等).该语法与标准SQL注释语法稍有不同. 从/序列到后面的/序列.结束序列不一定在同一行中,因此该语法允许注释跨越多行. 下面的例子显示了3种风格的注释: mysql> SELECT 1+1; # This comment continues to the end of li

C++学习笔记之函数指针

与数据项类似,函数也有地址.函数的地址是存储其机器语言代码的内存开始的地方. 一.函数指针的基础知识 假设要设计一个名为estimate()的函数,估算编写指定行数代码所需时间,并且希望不同的程序员都使用该函数,并且该函数允许每个程序员提供自己的算法来估计时间.为实现这种目标,采用的机制是,将程序员要使用的算法函数地址传给estimate(),必须完成以下工作: 获取函数地址 声明一个函数指针 用函数指针来调用函数 1.获取函数地址 使用函数名(后面不跟参数)即可.如:think()是一个函数,

golang学习笔记————字符串

字符串的创建 在golang中 字符串是使用双引号("")包裹住的字符序列: 字符是使用单引号('')包裹住的单一字符: 声明字符串的方式:var strvalue string 声明并创建的方式:strvalue := "This is a string!"; KeyPoint: 一旦字符串变量被初始化后,则不可单独改变该字符串序列中的某一字符:但该字符串变量可以重新被赋值: Ex:   strvalue := "abcd" fmt.Print