Go语言基础04(函数、方法)

一、函数

1.1 函数简介

  • 定义:函数是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。Go语言的函数可以分为:自定义函数系统函数
  • 作用:使用函数可以加强代码的复用性,提高程序编写的效率。

1.2 函数基本定义与使用

1.2.1 函数定义

  • Go语言中,使用函数前,必须先声明与定义函数。Go语言的函数由 关键字func函数名参数列表返回值函数体返回语句return组成。
  • Go语言是编译型语言,所以函数编写的顺序是无关紧要的,鉴于可读性的需求,最好把main()函数写在文件的前面,其他函数按照一定逻辑顺序进行编写(例如:函数被调用的顺序)。
  • Go语言函数定义格式如下:
    func funcName([formal_parameter_list]) ([return_types]) {
    	// 函数体...
    	// return valuelist
    }
    
    • func:定义函数所使用的关键字,所有的函数前面都必须使用该关键字。
    • funcName:函数的名称。
    • formal_parameter_list形式参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个被传递的值称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
      • 如果参数列表中若干个相邻的参数类型相同,则可以在参数列表中省略前面变量的类型声明,只需要写最后一个参数的类型即可。例如:func add(a, b int) (ret int, err error) { }
    • return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。有些功能不需要返回值,这种情况下return_types不是必须的。
    • 函数体:函数定义的代码集合。
    • return:函数返回值使用的关键字。
    • valuelist:函数返回值列表。

1.2.2 函数调用

  • 当创建函数时,定义了函数需要做什么,而调用函数则是执行指定任务。
  • 调用函数需要向函数传递参数(如果形式参数列表formal_parameter_list存在),并返回值(如果返回值类型return_types存在)。
  • Go语言调用函数格式如下:
    [return_valuelist] = funcName([actual_parameter_list])
    
    • return_valuelist:函数返回值列表。
    • actual_parameter_list:实际参数列表。
  • 调用函数,可以通过两种方式来传递参数(默认情况下,Go语言使用的是值传递,即在调用过程中不会影响到实际参数):
    • 值 传 递 \pmb{值传递} 值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改将不会影响到实际参数
    • 引 用 传 递 \pmb{引用传递} 引用传递:引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改将影响到实际参数
  • 注意:在定义函数时,形参与实参的个数与类型都要保持一致。

1.2.3 函数可变参数

  • 可变参数就是一个占位符,你可以将1个或者多个参数赋值给这个占位符,这样不管实际参数的数量是多少,都能交给可变参数来处理。
  • 可变参数使用name ...Type的形式声明在函数的参数列表中,而且需要是参数列表的最后一个参数。
  • 可变参数在函数中将转换为对应的[]Type类型,所以我们可以像使用切片(slice)时一样来获取传给函数的可变参数,即通过编号获取集合中存储的具体数据。
  • Golang的可变参数不需要强制绑定参数的出现,即不需要先指定至少一个固定的形参(num)才能使用...可变参数。
  • 传递参数给带有可变参数的函数有两种形式:
    • 第一种与通常的参数传递没有什么区别。
    • 第二种形式是使用...运算符以变量...的形式进行参数传递,这里的变量必须是与可变参数类型相同的切片(slice),而不能是其他类型(没错,数组也不可以)。示例如下:
      func sum(nums ...int) {
      	for key, value := range nums {
      		fmt.Printf("key:%d, value:%d\n", key, value)
      	}
      }
      
      func main() {
      	numbers := []int{2, 4, 6, 8, 10}
      	sum(numbers...)
      }
      
      输出:
      key:0, value:2
      key:1, value:4 
      key:2, value:6 
      key:3, value:8 
      key:4, value:10
      

1.2.4 函数变量作用域

  • 变量作用域表示已声明的标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。
  • 函数内定义的变量称为局部变量,形式参数和返回值变量也是局部变量。局部变量的作用域在函数内部。
  • 函数外定义的变量称为全局变量,全局变量可以在任何函数中使用。全局变量声明必须以var关键字开头。
  • 如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量。

1.2.5 函数返回值

  • Go语言中函数可以不返回任何值,也可以返回一个或者多个值。其他的编程语言的函数一般只可以不返回值或返回一个值,这是Go语言函数与其他编程语言函数的区别。当返回值是多个时,需要将return_types的列表使用小括号()括起来,不然语法会报错。
  • 如果相邻的几个返回值的类型相同,那么我们可以省略前几个返回值的类型,只需要写最后一个返回值的类型即可。例如:func arithmetic(a, b int) (add, sub, mul int, div float64) { }
  • 在函数返回多个值时,调用函数时,也必须使用相对于的参数个数来接受返回值,如果不需要的返回值,我们可以使用匿名变量来接受保存。
  • 匿名变量的特点是一个下画线“_”,“_”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时只需要在变量声明的地方使用下画线替换即可

1.2.6 defer 关键字

  • 在我们编写函数时,经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时释放资源,Go语言设计者提供了defer(延时机制)关键字。
  • 如果一个函数里面有多个defer语句,那么这些defer语句将会按照书写的逆序进行,即后进先出(LIFO),也就是说,最先被defer的语句最后被执行,最后被defer的语句最先被执行。
  • Go语言的defer语句一般都是用来处理需要关闭的资源。如果同一个函数中,既有defer语句,同时也有return语句,那么defer语句会在return语句的后面执行,即return最先给返回值赋值,接着defer开始执行一些收尾工作。

1.2.7 匿名函数(闭包)

  • Go语言匿名函数,就是只有函数体没有函数名的函数。格式如下:
    func ([formal_parameter_list]) ([return_types]) {
    	// 函数体...
    	// return valuelist
    }
    
  • Go语言的匿名函数可以作为一种类型直接赋值给变量,也可以作为函数的参数,传递给函数。格式如下:
    f = func([formal_parameter_list])([return_types]){
        // 函数体...
    	// return valuelist
    }
    
  • 匿名函数调用:
    • 定义的同时调用匿名函数:
      (直接在大括号{ }的后面写小括号( ),在小括号里面传入实参即可)
      func ([formal_parameter_list]) ([return_types]) {
      	// 函数体...
      	// return valuelist
      }(actual_parameter_list)
      
    • 使用变量调用匿名函数:
      f = func([formal_parameter_list])([return_types]){
          // 函数体...
      	// return valuelist
      }
      f(actual_parameter_list)
      

1.2.8 函数作为实参

  • Go语言可以很灵活的创建函数,并作为另外一个函数的实参。示例如下:
    func double(sum func(int, int) int) int {
    	return sum(2, 2) * 2
    }
    
    func main() {
    	add := func(x, y int) int {
    		return x + y
    	}
    	fmt.Println(double(add))
    }
    

1.3 案例

  • 模拟用户注册,当用户输入完用户名、密码和邮箱后,进行校验。如果发现用户名、密码或邮箱是空的,则给出“信息不能为空,用户注册为失败”的提示,否则,进行邮件发送,并给出“用户:XXX,注册成功!”的提示。
    func main() {
    	// 1. 用户注册
    	username, password, email := register()
    	// 2. 校验信息
    	b := checkInfo(username, password, email)
    	// 3. 发送邮件
    	sendEmail(username, b)
    }
    
    func register() (username string, password string, email string) {
    	fmt.Print("请输入用户名:")
    	fmt.Scanf("%s\n", &username)
    	fmt.Print("请输入密码:")
    	fmt.Scanf("%s\n", &password)
    	fmt.Print("请输入邮箱:")
    	fmt.Scanf("%s\n", &email)
    	return username, password, email
    }
    
    func checkInfo(username string, password string, email string) bool {
    	if username == "" || password == "" || email == "" {
    		return false
    	} else {
    		return true
    	}
    }
    
    func sendEmail(username string, b bool) {
    	if b {
    		fmt.Printf("用户:%s,注册成功!\n", username)
    	} else {
    		fmt.Println("信息不能为空,用户注册失败!")
    	}
    }
    

1.4 递归函数

  • 函数递归就是一个函数在函数体内又调用了自身,我们称为函数的递归调用。
  • Go语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。

1.4.1 案例1:斐波那契数列

  • 计算斐波那契数列,即前两个数为1,从第三个数开始每个数均为前两个数之和。
    func main() {
    	for i := 1; i <= 20; i++ {
    		fmt.Printf("%d\t", fibonacci(i))
    		if i%5 == 0 {
    			fmt.Println()
    		}
    	}
    }
    
    func fibonacci(num int) int {
    	if num < 2 {
    		return num
    	}
    	return fibonacci(num-1) + fibonacci(num-2)
    }
    

1.4.1 案例2:阶乘

  • 使用递归函数实现阶乘
    func main() {
    	var num = 10
    	fmt.Printf("%d的阶乘是%d", num, factorial(num))
    }
    
    func factorial(num int) int {
    	if num > 1 {
    		return num * factorial(num-1)
    	}
    	return 1
    }
    

1.5 预声明函数

  • close:关闭channel
  • delete:用于在map中删除实例。
  • len:用于返回字符串、切片长度。
  • cap:返回切片的最大容量。
  • new:用于各种类型的内存分配。
  • make:用于内建类型的内存分配。
  • append:向slice追加零值或其他值,并返回追加后新的与slice同类型的值。
  • copy:从源slice复制元素到目标,并返回复制元素的个数。
  • panicrecover:用于异常处理机制。
  • printprintln:底层打印函数,不用引入fmt包。
  • complexrealimag:用于处理复数。

二、方法(后续详解)

  • 方法的声明和函数类似,他们的区别是:方法在定义的时候,会在func方法名之间增加一个参数,这个参数就是接收者,这样我们定义的这个方法就和接收者绑定在了一起,称之为这个接收者的方法。格式如下:
    func (variable_name variable_data_type) funcName([formal_parameter_list]) ([return_types]) {
    	// 函数体...
    	// return valuelist
    }
    
    • variable_name:接收者名称。
    • variable_data_type:接收者类型。可以是 命名类型 / 结构体类型 / 指针
  • 示例:下面定义一个结构体类型和该类型的一个方法
    package main
    
    import (
    	"fmt"  
    )
    
    /* 定义结构体 */
    type Circle struct {
    	radius float64
    }
    
    func main() {
    	var c1 Circle
    	c1.radius = 10.00
    	fmt.Println("圆的面积 = ", c1.getArea())
    }
    
    //该 method 属于 Circle 类型对象中的方法
    func (c Circle) getArea() float64 {
    	// c.radius 即为 Circle 类型对象中的属性
    	return 3.14 * c.radius * c.radius
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

软耳朵DONG

觉得文章不错就鼓励一下作者吧

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

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

打赏作者

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

抵扣说明:

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

余额充值