Golang学习Day2

本文介绍了Go语言中的函数特性,包括普通函数、匿名函数和方法,强调了函数不能重载和不能嵌套但可以嵌套匿名函数的特点。还讲解了函数作为值、参数及返回值的用法,以及函数调用时参数的拷贝。此外,文中还讨论了Go中的返回值处理,函数参数的传址行为,变长参数,以及`type`关键字的作用。最后,文章涵盖了高阶函数、闭包、递归和`defer`语句的使用,以及`init`函数的初始化功能。
摘要由CSDN通过智能技术生成

Go语言中的函数

go语言中函数特性

  1. go语言有三种函数:普通函数、匿名函数(没有名称的函数)方法(定义在struct上的函数)。receiver

  1. go语言中不运算函数重载(overload),也就是说不允许函数同名

  1. go语言中的函数不能嵌套,但是可以嵌套匿名函数

  1. 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数

  1. 函数可以作为参数传递给另一个函数

  1. 函数的返回值可以是另一个函数

  1. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数

  1. 函数参数可以没有名称

go语言中函数的定义和调用

函数在使用之前必须先定义,可以调用函数来完成某个任务,函数可以重复调用,从而达到代码的复用

func function_name([parameter_list])[return_types]{
    函数体
}

package main

import "fmt"

func sum(a int, b int) (res int) {
    res = a + b
    return res
}
func main() {
    res := sum(1, 2)
    fmt.Printf("res: %v\n", res)
}
package main

import "fmt"

func compare(a int, b int) (max int) {
    if a > b {
        max = a
    } else {
        max = b
    }
    return max
}
func main() {
    res := compare(1, 2)
    fmt.Printf("res: %v\n", res)
}

go语言中的return

  1. 没有返回值

  1. 有一个返回值 (例如上面的求和以及比较两个数的大小的函数)

  1. 存在多个返回值,且在return中指定返回的内容

  1. 多个返回值,返回值名称没有被使用

  1. return覆盖命名返回值,返回值名称没有被使用

package main

import "fmt"

func test() {
    fmt.Print("testing...")
}
func main() {
    test()
}
package main

import "fmt"

func test() (string, int) {
    name := "Y4y17"
    age := 24
    return name, age
}
func main() {
    name, age := test()
    fmt.Printf("name: %v\n", name)
    fmt.Printf("age: %v\n", age)
}
package main

import "fmt"

func test() (name string, age int) {
    name = "Y4y17"
    age = 24
    return		//相当于return name,age
}
func main() {
    name, age := test()
    fmt.Printf("name: %v\n", name)
    fmt.Printf("age: %v\n", age)
}
package main

import "fmt"

func test() (name string, age int) {
    n := "Y4y17"	//重新定义了n和a,那么返回的时候只能返回n和a,而不是name和age
    a := 24			//这种情况一般不这样写,一般就是直接去掉返回值中的name和age,只保留类型
    return n, a
}
func main() {
    name, age := test()
    fmt.Printf("name: %v\n", name)
    fmt.Printf("age: %v\n", age)
}
  1. Go中经常会使用其中一个返回值作为函数是否执行成功、是否有错误信息的判断条件;例如return value,exists、return value,ok、return value,err等

  1. 当函数的返回值过多的时候,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如,同类型的返回值可以放进slice中,不同类型的返回值可以放进map中。

  1. 当函数有多个返回值的时候,如果其中某个或者是某几个返回值不想用,可以通过下划线_来丢弃这些返回值。

函数的参数

package main

import "fmt"

func test(a int) {
    a = 200	//test函数中修改变量a的值为200
}
func main() {
    a := 5	//定义变量a的值为5
    test(a)
    fmt.Printf("a: %v\n", a)//输出a的值,依然还是5
}

数组、切片、指针都是传地址,也就是传址的方式;

package main

import "fmt"

func test(s []int) {
    s[2] = 17
}
func main() {
    s := []int{1, 2, 3, 4}
    test(s)
    fmt.Printf("s: %v\n", s)//输出的结果为s: [1 2 17 4]
}

变长参数

package main

import "fmt"
//其实就像是传递了一个数组或者是切片
func test(args ...int) {
    for _, v := range args {
        fmt.Printf("v: %v\n", v)
    }
}
func main() {
    test(1, 2, 3, 4, 5, 6, 7)
}
package main

import "fmt"

func test(name string, b bool, args ...int) {
    fmt.Printf("name: %v\n", name)
    fmt.Printf("b: %v\n", b)
    for _, v := range args {
        fmt.Printf("v: %v\n", v)
    }
}
func main() {
    test("Y4y17", true, 1, 2, 3, 4, 5, 6, 7)
}

函数中的type关键字

相当于在c语言中的typedef关键字,说白了就是对现有的数据类型起了一个别名

package main

import "fmt"

func add(a int, b int) int {
    return a + b
}
func comp(a int, b int) int {
    if a > b {
        return a
    } else {
        return b
    }
}
func main() {
    type f func(a int, b int) int
    ff := add
    r := ff(1, 2)
    fmt.Printf("r: %v\n", r)
    ff = comp
    r = ff(1, 2)
    fmt.Printf("r: %v\n", r)
}

Go高阶函数

  1. 函数作为参数

package main

import "fmt"

func test(name string, f func(string)) {	
    f(name)
}
func sayHello(name string) {
    fmt.Printf("Hello: %v\n", name)
}
func main() {
    test("Y4y17", sayHello)
}

//test函数接受两个参数,分别是string类型的name和func(函数类型)的f(函数名)

sayHello函数接受一个参数,即string类型的name,主函数中调用test函数,传递参数Y4y17,和sayHello函数名;那么test函数中便会执行sayHello("Y4y17")

  1. 函数作为返回值

package main

import "fmt"

func add(a int, b int) int {
    return a + b
}
func sub(a int, b int) int {
    return a - b
}
func test(operate string) func(int, int) int {
    //test函数接受一个string类型的参数operate,test函数返回值是一个函数,该函数有两个int类型的参数并且返回值也是int类型
    
    switch operate {
    case "+":
        //当operate是+的时候,返回add方法
        return add
    case "-"://当operate是-的时候,返回sub方法
        return sub
    default:
        return nil
    }
}

func main() {
    f := test("-")
    res := f(1, 2)
    fmt.Printf("res: %v\n", res)
}

匿名函数

前面提到了在函数中是不允许嵌套函数的,但是我们可以使用匿名函数,来实现一些简单的功能:

package main

import "fmt"

func main() {
    //匿名函数和普通函数的区别其实就是没有了函数的名字,当在一个函数中不写函数名的时候就是匿名函数
    max := func(a int, b int) int {
        if a > b {
            return a
        } else {
            return b
        }
    }(1, 2)	//直接在最后面加上()以及实参的时候,就是自己调用自己
	//r := max(3, 8)	
	//fmt.Printf("r: %v\n", r)
	fmt.Printf("max: %v\n", max)
}

Golang闭包

闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁,或者是说函数和其引用环境的组合体

闭包指的是一个函数和与其相关的应用环境组合而成的实体。简单来说,闭包=函数+引用环境。看下面的例子:

package main

import "fmt"

//定义一个函数名为test,该函数返回值是一个函数,这个函数有一个int类型的参数 并且他返回值是一个int类型的数
func test() func(int) int {
    var x int
    return func(i int) int {
        x += i
        return x
    }
}
func main() {
    f := test()
    //f赋值为test的返回值,就是返回的函数,初始x为0
    fmt.Printf("f(10): %v\n", f(10))	//计算0+10=>10 这里x就变成了10 而不是0
    fmt.Printf("f(20): %v\n", f(20))	//计算10+20=>30 这里x就变成了30 而不是10
    fmt.Printf("f(30): %v\n", f(30))	//计算30+30=>60 这里x就变成了60 而不是30
}

变量f是一个函数并且他引用了其外部作用域中的x变量,此时f就是一个闭包。在f的生命周期内,变量x也一直有效;

package main

import (
    "fmt"
    "strings"
)

func makeSuffixFunc(suffic string) func(string) string {
    return func(name string) string {
        //这里的strings.HasSuff(name.suffic)表示判断name是否以suffic结尾
        //如果是返回true,否则false	需要导包strings
        if !strings.HasSuffix(name, suffic) {
            return name + suffic
        } else {
            return name
        }
    }
}
func main() {
    f := makeSuffixFunc("World")
    str := f("Hello")
    fmt.Printf("str: %v\n", str)
}
package main

import "fmt"

func cal(base int) (func(int) int, func(int) int) {
    add := func(a int) int {
        base += a
        return base
    }
    sub := func(a int) int {
        base -= a
        return base
    }
    return add, sub
}
func main() {
    add, sub := cal(10)
    res := add(10)
    fmt.Printf("res: %v\n", res)
    res = sub(5)
    fmt.Printf("res: %v\n", res)
}

定义了cal函数,cal函数存在一个形参base,cal函数返回值存在两个,并且这两个都是函数,之后在cal函数中定义两个匿名的函数分别赋值给add 和 sub,最终cal函数返回这两个匿名函数,之后在main函数中,令add sub为cal(10) ,其中base就是10 之后执行res=add(10),而这个10是匿名函数的形参a=10,执行base+=a故base=20

base的值会存储下来,之后res = sub(5),此时sub函数中的形参a就是5,再次执行base-=a,故得到base=15

Go语言中的递归函数

函数内部调用函数自身的函数称之为递归函数

使用递归函数最重要的三点:

  1. 递归就是自己调用自己

  1. 必须先定义函数的退出条件,没有退出条件,递归将成为死循环

  1. go语言递归函数很可能会产生一大堆的goroutine,也很可能会出现占空间内存溢出的问题

package main

import "fmt"

//数的阶乘
func fc(n int) int {
    if n <= 1 {
        return 1
    } else {
        res := n * fc(n-1)
        return res
    }
}
func main() {
    res := fc(5)
    fmt.Printf("res: %v\n", res)
}
package main

import "fmt"
//f(n)=f(n-1)+f(n-2) f(2)==f(1)==1
func fb(n int) int {
    if n <= 2 {
        return 1
    } else {
        res := fb(n-1) + fb(n-2)
        return res
    }
}
func main() {
    fmt.Printf("fb(3): %v\n", fb(3))
}

Go语言中的defer语句

go语言中的defer语句会将其后面跟随的语句进行延时处理,在defer归属的函数即将返回时,姜堰市处理的语句按defer定义的逆序进行执行;也就是说先被defer的语句最后被执行,最后被defer的语句,最先被执行

package main

import "fmt"

func main() {
    fmt.Printf("start...\n")
    defer fmt.Printf("stop1...\n")
    defer fmt.Printf("stop2...\n")
    defer fmt.Printf("stop3...\n")
    fmt.Printf("END...\n")
}

输出结果如下:

Go语言中的init函数

golang有一个特殊的函数init函数,先于main函数的执行,主要实现包级别的一些初始化操作

init函数额主要特点

  1. init函数先于main函数自动执行,不能被其他函数调用

  1. init函数没有输入参数、返回值

  1. 每个包可以有多个init函数

  1. 包的每个源文件也可以有多个init函数,这点比较特殊

  1. 同一个包的init执行顺序,golang没有明确的定义;

  1. 不同包的init函数按照包导入的依赖关系决定执行顺序

初始化顺序:变量初始化>init()>main()

package main

import "fmt"

var i int = initVar()

func initVar() int {
    fmt.Printf("initvar...\n")
    return 100
}
func init() {
    fmt.Printf("init2...\n")
}
func init() {
    fmt.Printf("init...\n")
}
func main() {
    fmt.Printf("start...\n")
}

输出结果为:

同时存在多个init()的时候,遵循自上而下的执行顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y4y17

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值