Go 函数语法规则 | Go语言函数编程全解析

silverwq
2022-07-12 / 0 评论 / 302 阅读 / 正在检测是否收录...

函数的定义

函数的定义使用 func 关键字定义

func 函数名(参数)(返回值){
    函数体
}

函数名:由 字母、数字、下划线 组成,但函数名的第一个字母不能是数字,在同一个包内,函数名也称 不能重复
参数由:参数变量参数变量的类型 组成,例如:(amount int,name string),多个参数之间使用 , 分隔,可以没有参数;当参数比较长的时候,可以定义为结构体。
返回值:由变量和类型的形式,返回值变量可以在函数体中被使用,此时 return 语句后可为空,将定义的返回变量给返回,也就是将 return 变量值返回:

func intSum(x int,y int) (result int) {
    result = x + y
    return // return语句后可为空
}

返回值参数还可以只有类型的形式,这个时候 return 语句后不能为空

// 只有int类型,没有变量
func intSum(x int,y int) int {
    return x + y // 这个时候return语句后不能为空
}

函数还可以返回多个值,或者没有返回值

// 返回两个int类型的值
func test () (int,int) {
    return 1,2
}

函数的参数

如果相邻变量的类型相同,则可以省略类型

// x类型也是int,和y一样
func intSum(x, y int) int {
    return x + y
}

还可以声明为可变参数,在参数名后加 来标识,可变参数通常要作为函数的最后一个参数

func main() {
    ret := intSum(10, 20)
    fmt.Println(ret)
}
// 求和
func intSum(x ...int) int {
    fmt.Println(x) //x是一个切片
    sum := 0
    for _, v := range x {
        sum = sum + v
    }
    return sum
}

声明指针类型的参数,传入的值必须是指针

// Person 是一个结构体
type Person struct {
    name string
    age  int8
}
func test(p *Person)  {

}
func main() {
    p1 := &Person{
 name: "张三",
   age:  30,
    }
    test(p1)
}

函数参数必须传,但是参数可以没有被使用

func test(name string) {  // name 没有被使用是允许的
   fmt.Println("123")  
}  

func main() {  
   test("123")  // 但是参数必须传递
}

函数参数的默认值是如何设置的呢?

方式一:使用中间对象的方式

// 默认参数的选项,中间对象  
type TeaOptions struct {  
   heat bool  
}  

// 创建默认参数对象  
func NewDaulft() *TeaOptions {  
   return &TeaOptions{  
      heat: false, // 默认不加热  
   }  
}  

type Tea struct {  
   name string  
   heat bool  
}  

// 创建具体对象  
func NewTea(name string, ops *TeaOptions) (*Tea, error) {  
   return &Tea{  
      name: name,  
      heat: ops.heat,  
   }, nil  
}  

func (t Tea) getTea() {  
   fmt.Printf("name:%s , heat:%t ", t.name, t.heat)  
}  

func main() {  
   // 使用默认参数  
   tea1, _ := NewTea("tea1", NewDaulft())  
   tea1.getTea()  
   // 不使用默认参数,创建TeaOptions对象传参  
   tea2, _ := NewTea("tea2", &TeaOptions{heat: true})  
   tea2.getTea()  
}

方式二:使用 选项模式,该模式便于扩展,可以使用设计模式中的选项模式来创建对象

type config struct {  
   logicalRegionIdKey   string  
   logicalRegionNameKey string  
}  

type Option func(cfg *config)  

// WithSetLogicalRegionIdKey 修改默认值  
func WithSetLogicalRegionIdKey(key string) Option {  
   return func(cfg *config) {  
      cfg.logicalRegionIdKey = key  
   }  
}  

func NewLogicalRegionConverter(name string, opts ...Option) {  
   // 默认值  
   cfg := config{  
      logicalRegionIdKey:   "logical_region_id",  
      logicalRegionNameKey: "logical_region_name",  
   }  
   if opts != nil && len(opts) > 0 {  
      for _, opt := range opts {  
         opt(&cfg)  
      }  
   }  
   fmt.Println(name, cfg.logicalRegionIdKey, cfg.logicalRegionNameKey)  
}  

func main() {  
   NewLogicalRegionConverter("区域配置")  
   NewLogicalRegionConverter("区域配置", WithSetLogicalRegionIdKey("logical_region_id_不是默认值"))  
}

函数的返回值

函数可以有多个返回值,有多个返回值时必须用 () 将所有返回值包裹起来

// (int,int)
func calc(x, y int) (int, int) {
    sum := x + y
    sub := x - y
    return sum, sub
}

可以对返回值进行命名,在函数体中直接使用这些变量,最后通过 return 关键字返回

func calc(x, y int) (sum, sub int) {
    sum = x + y
    sub = x - y
    return
}

函数的调用

通过 函数名() 的方式调用函数,调用有返回值的函数时,可以不接收其返回值

func main() {
    // 调用
    sayHello()
    ret := intSum(10, 20)
    fmt.Println(ret)
}

函数参数有时候很长,需要换行显示,最后一个参数需要有逗号,这样好像也没关系

func QueryByPage(
  logicalRegionId int,
    page, pageSize int, // 需要有逗号
) {
  // 实现
}

函数的作用域

在函数中可以访问到全局变量,也就是定义在函数外部的全局变量,在函数内部定义的局部变量只有当前函数作用域中生效,如果局部变量和全局变量重名,优先访问局部变量。

函数类型的变量

可以将变量声明为函数类型

type calculation func(int, int) int

// 例如,就是calculation类型
func add(x, y int) int {
    return x + y
}

func main() {
    var c calculation               // 声明一个calculation类型的变量c
    c = add                         // 把add赋值给c
    fmt.Printf("type of c:%T\n", c) // type of c:main.calculation
    fmt.Println(c(1, 2))            // 像调用add一样调用c

    f := add                        // 将函数add赋值给变量f1
    fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int
    fmt.Println(f(10, 20))          // 像调用add一样调用f
}

函数作为参数

既然函数可以作为变量类型,并且用于赋值,那么函数自然可以传递给函数的参数

func add(x, y int) int {
    return x + y
}
// 接收函数类型为:func(int, int) int的函数
func calc(x, y int, op func(int, int) int) int {
    return op(x, y)
}
func main() {
    ret2 := calc(10, 20, add)
    fmt.Println(ret2) //30
}

函数作为返回值类型

同理,返回值类型也可以定义为函数类型

func add(x, y int) int {
    return x + y
}
// 返回类型:func(int, int) int
func do() (func(int, int) int) {
    return add
}

匿名函数

我们把没有函数名称的函数,叫做匿名函数,这个跟 php 的匿名函数类似

func main() {
    // 将匿名函数保存到变量
    add := func(x, y int) {
        fmt.Println(x + y)
    }
    add(10, 20) // 通过变量调用匿名函数

    //自执行函数:匿名函数定义完加()直接执行
    func(x, y int) {
        fmt.Println(x + y)
    }(10, 20)
}

闭包

闭包是一个 函数与其相关的引用环境 组合而成的实体,就好比是封闭的一个包,有点像类的,里面有属性和方法。

// 类似于一个类:属性+方法
func adder(x int) func(int) int {
    // 属性
    var z int
    // 函数
    return func(y int) int {
        z = y + x + z
        x++
        return z
    }
}
func main() {
    // 属性x、z在f对象的整个声明周期内有效
    var f = adder(10) // 构造函数初始化
    fmt.Println(f(20)) //30 调用对象的方法
    fmt.Println(f(50)) //91
}

defer 语句

defer 语句会将其后面跟随的语句进行延迟处理,函数即将返回时,按 defer 定义的逆序进行执行

// 输出:start end 3 2 1
func main() {
    fmt.Println("start")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("end")
}
0

评论 (0)

取消