Go语言 函数

2018-08-18  本文已影响0人  小杰的快乐时光

函数的一般结构组成如下所示

func function_name( [parameter list] ) [return_types] {
   函数体
}
函数定义解析:
* func:函数由 func 开始声明
* function_name:函数名称,函数名和参数列表一起构成了函数签名。
* parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
* return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
* 函数体:函数定义的代码集合。

比如下面这个返回int类型的函数

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 声明局部变量 */
   var result int
   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

函数的多返回值

func main() {
   fmt.Println(Add(1,2))
}

func Add(a, b int) (sub int, err error) {
   if a < 0 || b < 0 {
      err = errors.New("a,b不同为负数")
      return 0,err
   }
   return a+b,nil
}
----output-----
3 <nil>

关于函数的返回值命名
Go语言中,返回值可以像被变量那样使用,在return没有返回值参数的情况下,会直接返回当前的计算值结果
但是一般不在长函数中使用,会影响代码可读性。

func main() {
   fmt.Println(sub(1))
}

func sub(num int)(x,y int)  {  //返回值是x,y
   if num >0 {
      x = num+2  //这里的x,y就是返回值中的x,y,被当作变量使用
      y = num-3
      return  //会直接返回x,y的计算值结果,与return x,y同效
   }
   return  //会返回int的默认值:0 , 0,与return 0,0同效
}

递归函数
Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。

语法格式如下:

func recursion() {
   recursion() /* 函数调用自身 */
}

func main() {
   recursion()
}

计算阶乘
①普通for循环计算15的阶乘

func main() {
   var j int  = 1
   for i:=15;i>0 ;i--  {
      j *=i
   }
   fmt.Println(j)
}
---output---
1307674368000

②使用Go的递归函数

func Factorial(n int)(result int) {
   if n > 0 {  //跳出递归的条件
      result = n * Factorial(n-1) /* 函数调用自身 */
      return result
   }
   return 1
}

func main() {
   var i int = 15
   fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(i))
}

匿名函数与闭包
闭包就是一个函数“捕获”和它在同一作用域的其他常量或变量。在Go语言中所有的匿名函数都是闭包。闭包的创建方式与普通函数在语法上几乎一致,但是闭包没有名字,通常都是将闭包赋值给一个变量,或者放到一个映射或切片中。

func main() {
   c1 := f(0)
   c2 := f(0)
   fmt.Println(c1() )  // 1
   fmt.Println(c1() )  // 2
   fmt.Println(c2() )  // 1
}

// 闭包使用方法
func f(i int) func() int {
   return func() int {
      i++
      return i
   }
}

分析:c1跟c2引用的是不同的环境,在调用i++时修改的不是同一个i,因此两次的输出都是1。函数f每进入一次,就形成了一个新的环境,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。

下面这个例子中,每次调用的都是add_func(),因此i会递增

func main() {
   add_func := add(1,2)
   fmt.Println(add_func())
   fmt.Println(add_func())
   fmt.Println(add_func())
}

// 闭包使用方法
func add(x1, x2 int) func()(int,int)  {   
   i := 0
   return func() (int,int){  //匿名函数
      i++
      return i,x1+x2
   }
}
----------output------------  只要闭包的还被使用,那么在闭包的变量会一直存在,比如 匿名函数中的i值
1 3
2 3
3 3

我们来看看一个使用闭包匿名函数与普通调用外部函数的区别

func main() { //普通调用外部函数
   var j int  = 5
   a:= add(j) //这里得到一个函数a,i=10,j=5,里面有一个 fmt.Println(i,j)
   a() //这里 调用 fmt.Println(i,j),输出 10,5
   j*=2 //将j乘以2
   a() //这里依旧是以前的函数,i=10,j=5,j没有受影响
   a1:= add(j)  //这里将重新获得一个函数,此时i=10,j = 10
   a1() //输出10,10
}

func add(j int)func()  {
   var i int  = 10
   return func() {
      fmt.Println(i,j)
   }
}
-----output------
10,5
10,5
10,10

上面的 a在创建时引用的环境与a1引用的环境不同,而又因为j是值传递,因此在a创建时引用的环境中调用的是j的副本,这跟原本j的值是否改变无关。创建a1后,会引入新的j值的副本,因此a不会受影响,而a1会受影响

func main() { 
   var j int  = 5
   a:= func() (func()){ //使用闭包匿名函数
      var i int  = 10
      return func() {
         fmt.Println(i,j)
      }
   }()  //这里加一个括号表示 函数调用
   a()
   j *= 2
   a()
}
-----output------  //这里输出受到了影响
10 5 
10 10

这里闭包会捕获j的值,没有值传递的操作,因此在j的值改变后,调用a会受到影响

上一篇下一篇

猜你喜欢

热点阅读