Go 語言編程 — 函數

目錄

定義一個函數

函數聲明需要指定

  • 函數的名稱
  • 形參列表
  • 返回值列表

函數名和形參列表一起構成函數簽名。格式:

func function_name([parameter list]) [return_types] {
   函數體
}

示例:

/* 函數返回兩個數的最大值 */
func max(num1, num2 int) int {
   /* 聲明局部變量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

形參列表

值傳遞

值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。默認情況下,Go 語言使用的是值傳遞,即在調用過程中不會影響到實際參數。

示例:

package main

import "fmt"

func main() {
   /* 定義局部變量 */
   var a int = 100
   var b int = 200

   fmt.Printf("交換前 a 的值爲 : %d\n", a)
   fmt.Printf("交換前 b 的值爲 : %d\n", b)

   /* 通過調用函數來交換值 */
   swap(a, b)

   fmt.Printf("交換後 a 的值 : %d\n", a)
   fmt.Printf("交換後 b 的值 : %d\n", b)
}

/* 定義相互交換值的函數 */
func swap(x, y int) int {
   var temp int

   temp = x /* 保存 x 的值 */
   x = y    /* 將 y 值賦給 x */
   y = temp /* 將 temp 值賦給 y*/

   return temp;
}

引用傳遞

引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。類似於 C 語言函數定義中的指針類型形參。

引用傳遞方式,將指針參數傳遞到函數內,以下是交換函數 swap() 使用了引用傳遞:

package main

import "fmt"

func main() {
   /* 定義局部變量 */
   var a int = 100
   var b int= 200

   fmt.Printf("交換前,a 的值 : %d\n", a)
   fmt.Printf("交換前,b 的值 : %d\n", b)

   /* 調用 swap() 函數
   * &a 指向 a 指針,a 變量的地址
   * &b 指向 b 指針,b 變量的地址
   */
   swap(&a, &b)

   fmt.Printf("交換後,a 的值 : %d\n", a)
   fmt.Printf("交換後,b 的值 : %d\n", b)
}

func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保存 x 地址上的值 */
   *x = *y      /* 將 y 值賦給 x */
   *y = temp    /* 將 temp 值賦給 y */
}

返回值

Go 函數可以返回多個值,例如:

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

回調函數

回調函數,即一個函數作爲另外一個函數的實參。所以,在傳遞一個函數之前,首先需要創建一個函數變量。這與 C 語言中的函數指針(指向函數的指針)類似。

package main

import (
   "fmt"
   "math"
)

func main(){
   /* 聲明函數變量 */
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   /* 使用函數 */
   fmt.Println(getSquareRoot(9))

}

再一個示例:

package main
import "fmt"

// 聲明一個函數類型
type cb func(int) int

func main() {
    testCallBack(1, callBack)
    testCallBack(2, func(x int) int {
        fmt.Printf("我是回調,x:%d\n", x)
        return x
    })
}

func testCallBack(x int, f cb) {
    f(x)
}

func callBack(x int) int {
    fmt.Printf("我是回調,x:%d\n", x)
    return x
}

閉包(Closure)函數

閉包(Closure):如果內層函數引用了外層函數的局部變量,並且在外層函數中 return 內層函數,這種關係就稱之爲閉包。

可見,閉包的特點是發生在函數嵌套的基礎上實現。外層函數返回的內層函數還引用了外層函數的局部變量,所以要想正確的使用閉包,那麼就要確保這個被內層函數引用的局部變量是不變的。Python 中使用閉包函數來實現了裝飾器機制。

Golang 支持匿名函數,可用於實現閉包。匿名函數是一個表達式嗎,其優越性在於可以直接使用函數內的變量,而不必聲明函數名。以下示例中,定義了函數 getSequence() 並返回了另外一個函數。getSequence() 函數的目的是在閉包中遞增 i 變量。

package main

import "fmt"

/**
 * getSequence 函數作爲外存函數,其返回值爲匿名函數 func() int,
 * 匿名函數 func() int 作爲內層函數,直接使用了外層函數的局部變量 i,
 * 如此形成了一個閉包。
 */
func getSequence() func() int {
    i := 0
    return func() int {
        i += 1
        return i  
    }
}

func main(){
    /* 定義一個函數變量 nextNumber,當前 i 變量爲 0 */
    nextNumber := getSequence()  

    /* 傳入函數作爲實參,i 變量自增 1 並返回 */
    fmt.Println(nextNumber())
    fmt.Println(nextNumber())
    fmt.Println(nextNumber())
   
    /* 創建新的函數 nextNumber1,並查看結果 */
    nextNumber1 := getSequence()  
    fmt.Println(nextNumber1())
    fmt.Println(nextNumber1())
    fmt.Println(nextNumber1())
}

結果:

1
2
3
1
2
3

方法函數

在常規的面向對象編程語言(OOP)中,比如 Python。函數(Function)和方法(Method)是屬於兩個不同的術語:在類中定義的稱爲成員方法,不在類中定義的稱爲獨立函數。

我們知道 Golang 不是一種 OOP 類型編程語言,但 Golang 也同時提供了方法和函數的概念:一個方法就是一個包含了接受者的函數,接受者可以是命名類型或者結構體類型的一個值或一個指針。

所有給定類型的方法屬於該類型的方法集。語法格式如下:

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函數體*/
}

示例:

package main

import "fmt"

/* 定義結構體 */
type Circle struct {
    radius float64
}

//該 method 屬於 Circle 類型對象中的方法
func (c Circle) getArea() float64 {
    // c.radius 即爲 Circle 類型對象中的屬性
    return 3.14 * c.radius * c.radius
}

func main() {
    var c1 Circle
    c1.radius = 10.00
    fmt.Println("圓的面積 = ", c1.getArea())
}

從上述例子可見,方法 getArea 就像是變量 c1 的成員屬性一般,類似於 Python 類對象的一個成員方法。

遞歸函數

遞歸,就是在運行的過程中調用自己。Golang 支持遞歸調用,但我們在使用遞歸時,需要合理設置退出條件,否則遞歸將陷入無限循環中。

格式:

func recursion() {
   recursion() /* 函數調用自身 */
}

func main() {
   recursion()
}

遞歸函數對於解決數學上的問題是非常有用的,就像計算階乘,生成斐波那契數列等。

  • 階乘:
package main

import "fmt"

func Factorial(n uint64) (result uint64) {
    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(uint64(i)))
}
  • 斐波那契數列:
package main

import "fmt"

func fibonacci(n int) int {
  if n < 2 {
   return n
  }
  return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
    var i int
    for i = 0; i < 10; i++ {
       fmt.Printf("%d\t", fibonacci(i))
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章