函数

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

addcompare
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

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
sliceslice...
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
f1intint
f1f1f1

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)
}
 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
延迟deferdefer逆序deferdefer

特性

deferretrundeferdeferdefer

用途

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

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函数
initmain

特点

  • 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...