Golang学习笔记—函数

函数

Go语言中并没有类(class),所以并不是纯粹的面向对象语言。大多数都是用函数、结构体实现。

特性

  • go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。
  • go语言中不允许函数重载(overload),即:不允许函数同名。
  • go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
  • 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
  • 函数可以作为参数传递给另一个函数。
  • 函数的返回值可以是一个函数。
  • 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
  • 函数参数可以没有名称。

函数定义

func function_name([parameter list])  [return_types]{
	函数体
}
  • func:函数由func开始声明

  • function_name:函数名称,函数名和参数列表一起构成了函数签名。

  • [parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指

    定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。

  • return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。(非必须可以不加)

  • 函数体:函数定义的代码集合。

Demo01

定义了两个函数add累和,compare比较最大值

package main

import "fmt"

func add(a int, b int) (sum int) {
	sum = a + b
	return sum
}
func compare(x int, y int) (max int) {
	if x > y {
		max = x
	} else {
		max = y
	}
	return max
}
func main() {
	sum := add(2, 3)
	fmt.Println(sum)
	max := compare(2, 3)
	fmt.Println(max)
}

若没设置retrun的返回内容,则根据返回类型返回值

package main

import "fmt"

func test() (name string, age int) {
   name = "Sentiment"
   age = 18
   return						//相当于return name, age
}
func main() {
   a, b := test()
   fmt.Println(a, b)
}

结果:

Sentiment 18

变长参数

如果函数的最后一个参数是采用 ...type 的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变长函数。

Demo02

package main

import "fmt"

func add(nums ...int) (res int) {
   for _, n := range nums {
      res += n
   }
   return
}
func main() {
   var result int
   result = add(1, 2, 3)
   fmt.Println(result)
}

//6

如果参数被存储在一个 切片类型的变量 slice 中,则可以通过 slice... 的形式来传递参数调用变参函数

package main

import "fmt"

func add(nums ...int) (res int) {
   for _, n := range nums {
      res += n
   }
   return
}

func main() {
   var result int
   slice := []int{7, 9, 3, 5, 1}
   result = add(slice...)
   fmt.Println(result)
}

//25

函数类型与函数变量

可以用type关键字来定义函数的类型

type f1 func( int, int) int

定义了一个f1函数类型,可以接受两个int类型的参数并且返回一个int类型的返回值

若定义一个与f1函数类型相同的函数,则可以把该函数赋值给f1,此时f1就有了该函数的功能

Demo03

package main

import "fmt"

type f1 func(int, int) int

func sum(a int, b int) int {
   return a + b
}

func max(a int, b int) int {
   if a > b {
      return a
   } else {
      return b
   }
}

func main() {
   var f1 f1
   f1 = sum 	//将sum函数赋值给f1类型的变量f1
   s := f1(1, 2)
   fmt.Println(s)
   f1 = max 	//将max函数赋值给f1类型的变量f1
   m := f1(3, 4)
   fmt.Println(m)
}

结果:

3
4

将函数作为参数

其实就是函数回调。

package main

import "fmt"

func sayhello(name string) {
   fmt.Printf("Hello,%s", name)
}

func test1(name string, f func(string2 string)) {
   f(name)
}
func main() {
   test1("Sentiment", sayhello)
}

匿名函数

所谓匿名函数就是没有名称的函数。

func(参数列表)(返回值)

Demo05

package main

import "fmt"

func main() {
   max := func(a int, b int) int {
      if a > b {
         return a
      } else {
         return b
      }
   }
   i := max(1, 2)
   fmt.Println(i)
}

//2

或者也可以直接调用自己

package main

import "fmt"

func main() {
	func(a int, b int) {
		max := 0
		if a > b {
			max = a
		} else {
			max = b
		}
		fmt.Println(max)
	}(1, 2)
}

//2

闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

Demo06

package main

import "fmt"

func addupper() func(x int) int {
   n := 10
   return func(x int) int {
      n += x
      return n
   }
}

func main() {
   f := addupper()
   fmt.Println(f(1))	//11
   fmt.Println(f(2))	//13
   fmt.Println(f(3))	//16
}
  • 代码说明,addupper是一个函数,返回数据类型是func (x int) int

  • 闭包部分:

n := 10
return func(x int) int {
  n += x
  return n

返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数和n形成了一个整体,构成闭包。

  • 当我们反复调用f函数时,因为n是初始化一次,因此每次调用都进行累加
  • 要搞清楚闭包的关键,就要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包

闭包实践

请编写一个程序,具体要求如下:

  • 编写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(如.jpg),并返回一个闭包
  • 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回文件名
  • 要求使用闭包的方式完成
package main

import (
   "fmt"
   "strings"
)

//- 编写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(如.jpg),并返回一个闭包
//- 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回文件名
//- 要求使用闭包的方式完成

func makeSuffix(suffix string) func(s string) string {
   return func(s string) string {
      if !strings.HasSuffix(s, suffix) {
         return s + suffix
      } else {
         return s
      }
   }
}

func main() {
   f := makeSuffix(".jpg")
   x := f("Sentiment")
   y := f("Sentiment.jpg")
   fmt.Println(x)
   fmt.Println(y)
}

结果:

Sentiment.jpg
Sentiment.jpg

也可以不用闭包实现,看下区别:

package main

import (
	"fmt"
	"strings"
)


func makeSuffix2(suffix string, s string) string {
	if !strings.HasSuffix(s, suffix) {
		return s + suffix
	} else {
		return s
	}
}

func main() {


	f1 := makeSuffix2(".jpg", "Sentiment")			//设置后缀.jpg
	f2 := makeSuffix2(".jpg", "Sentiment.jpg")		//设置后缀.jpg
	fmt.Println(f1)
	fmt.Println(f2)
}

这种方式在调用时,每次都需要设置后缀.jpg,而闭包只需要设置一个 f := makeSuffix(".jpg")即可

递归

  1. 递归就是自己调用自己。
  2. 必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
  3. go语言递归函数很可能会产生一大堆的goroutine,也很可能会出现栈空间内存溢出问题。

用个经典的斐波那契数列举例:

计算公式为f(n)=f(n-1)+f(n-2)f(2)=f( 1)=1

package main

import "fmt"

func main() {
   fmt.Println(fib(4))
}

func fib(n int) (res int) {
   if n == 1 || n == 2 {
      res = 1
   } else {
      res = fib(n-1) + fib(n-2)
   }
   return res
}

//3

defer

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

特性

  • 关键字defer用于延迟调用
  • 这些调用知道retrun前才被执行。因此,可以用来资源清理
  • 多个defer语句,按先进后出的方式执行
  • defer语句中的变量,在defer声明时就决定了

用途

  • 关闭文件句柄
  • 锁资源释放
  • 数据库链接释放

Demo08

package main

import "fmt"

func main() {
   fmt.Println("start")
   defer fmt.Println("step1")
   defer fmt.Println("step2")
   defer fmt.Println("step3")
   fmt.Println("end")
}

结果:

start
end
step3
step2
step1

ini函数

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

特点

  • init函数先于main函数自动执行,不能被其他函数调用
  • init函数没有输入参数,返回值
  • 每个 可以有多个init函数
  • 包的每个源文件也可以有多个init函数,这点比较特殊
  • 同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不用依赖这个执行顺序
  • 不同包的init函数按照爆导入的依赖关系决定执行顺序

初始化顺序

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

Demo09

package main

import "fmt"

var a int = initVar()

func init() {
   fmt.Println("init2")
}
func init() {
   fmt.Println("init")
}
func initVar() int {
   fmt.Println("init var...")
   return 100
}
func main() {
   fmt.Println("main...")
}

结果:

init var...
init2
init
main...
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值