iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
自我挑戰組

Go in 3o系列 第 12

[Day13] Go in 30 - 函式 - 參數不定函式、匿名函式與閉包

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20230927/20162693foa71C6T4A.jpg

一、本篇提要

本篇將會繼續咱有關Go語言函式的種種,今日介紹的是 :

  • 參數不定函式(variadic function)
  • 匿名函式與閉包

二、參數不定函式 variadic function

所謂參數不定函式,指的就是一個函式可以接收數量不確定的參數,使用時機就是當你無法確認某個參數要接收的引數有多少個。

典型不定參數寫法,就是型別前面加三點...,這個點在這邊也有屬於它的名字,打包算符(pack package)
用來表示可接收不定數量的引數,不定數量也包括完全沒有引數

func f(參數名稱...型別)

而它的作用實際上就是把所有符合該型別的引數都放進此參數名稱,打包成一個切片

如下範例 :

package main

import "fmt"

func main() {
    nums(10,99)
    nums(100,990)
    nums()
}

func nums(i...int) {
    fmt.Println(i)
}

輸出結果 :
https://ithelp.ithome.com.tw/upload/images/20230927/20162693dpQWs8aBAK.png

參數不定函式也可以有其他參數,但數量不定的一定要放在所有參數後面喔!!!

以下為錯誤範例 :

package main

import "fmt"

func main() {
    nums(10,99,"Ricky")
    nums(100,990, "Jack")
    nums("Lily")
}

func nums(i...int, name string) {
    fmt.Println(i, name)
}

https://ithelp.ithome.com.tw/upload/images/20230927/20162693ayTzfSXJqp.png

正確寫法 :

package main

import "fmt"

func main() {
    nums("Ricky",10,99)
    nums("Jack",100,990)
    nums("Lily")
}

func nums(name string, i...int) {
    fmt.Println(name, i)
}

輸出結果:
https://ithelp.ithome.com.tw/upload/images/20230927/20162693Yml6u6fBGP.png

就剛剛所說,不定參數函式,會將參數集結成切片,但如果直接將切片當引數傳入呢 ?

package main

import "fmt"

func main() {
    i := []int{5, 10, 15}
    nums(i) //傳入切片
}

func nums(i...int) {
    fmt.Println(i)
}

答案是會產生錯誤 : []int型別不能傳給int型別參數

蛤 ? 這樣剛剛說會集結成切片是咋回事 ? 好,其實是參數不定函式是接收一連串不定數量的值,再轉成切片。

那麼這個案例怎麼玩 ? 我們可以用...,它在這叫做unpack operator,意思就是先將[]int解開再傳入,這樣等同傳入值 nums(5,10,15)。注意此...與參數不定函式的...意義不同。

package main

import "fmt"

func main() {
    i := []int{5, 10, 15}
    nums(i...) //傳入切片 解包算符unpack operator
}

func nums(i...int) {
    fmt.Println(i)
}

三、匿名函式與閉包

到目前我們都式用具名函式居多,也就是有名字的且在package層宣告的函式,那還有一種沒有名稱的,叫做匿名函式(anonymous functions) 或稱函式常值(function literals)。

3.1 匿名函式

匿名函式 :

  1. 沒有名字
  2. 只能使用一次(除非我們在建立它後指派給一個變數)
  3. 只能在其他函式內宣告

匿名函式可以搭配以下用途或功能 :

  1. 定義只使用一次的函式。
  2. 定義要傳回給另一個函式的函式。
  3. 定義Goroutine的程式碼區塊(之後解釋)。
  4. 實作閉包
  5. 搭配defer敘述延後執行程式碼

匿名函式 vs 具名函式,宣告上的差異就是 : 有無名字。

package main

import "fmt"

func main() {
    //宣告匿名函式
    func() {
        fmt.Println("Hi~")
    }() // 用()立即呼叫
}

關於那個小括號,稱為執行小括號(execution parenthese)。這對小括號會當場呼叫匿名函式執行它,謠傳給函式的引數必須寫在執行小括號。

就像這樣:

package main

import "fmt"

func main() {
    name := "Ricky"
    func(str string) {
        fmt.Println("Hi~" + str)
    }(name) 
}

以上就是立即執行、且只能用一次的匿名函式,之後篇幅會介紹如何把它存在變數的不同運作方式。

範例 :

package main

import "fmt"

func main() {
     // 定義匿名函數並將其指派給變數 add
     add := func(x, y int) int {
         return x + y
     }

     // 呼叫匿名函數
     result := add(3, 5)

     fmt.Printf("結果是:%d\n", result)  //結果是 8
}

以上就是展示將匿名函式賦值給變數,再利用該變數來使用函式。

3.2 鍵利閉包

由匿名函式應用擴展到閉包的部分。

閉包,就是匿名函式的諸多形式之一,一般函式在離開某個函式的範圍後,就沒辦法繼續引用父函式的區域變數,但閉包可以。

一般匿名函式 :

package main

import "fmt"

func main() {
    i := 0
    increment := func() int {
        i++
        return i
    }
    fmt.Println(increment())
    fmt.Println(increment())
    i+=10
    fmt.Println(increment())
}

結果 :

https://ithelp.ithome.com.tw/upload/images/20230927/20162693zuZJ8w3ntW.png

但是如果increment()是宣告在其他函式裡 :

package main

import "fmt"

func main() {
    
    increment := incrementor() //接收傳回的函式
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
}

func incrementor() func() int {

    i := 0
    return func() int {
        i++
        return i
    }
}

https://ithelp.ithome.com.tw/upload/images/20230927/20162693bUhX7ktTHT.png

這裡的 increment 就是語意閉包(lexical closure)或就簡稱閉包,因為這個函式包住了他所引用的外部變數。換句話說,閉包能記住父函式的變數,即便離開父函式的執行範圍

閉包可以記住外部變數的這個特性,我們可以加以利用。

一個倒數計數器的練習 :

package main

import "fmt"

func main() {
    
    max:=10

    increment := decrementor(max) 
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
    fmt.Println(increment())
}

func decrementor(i int) func() int {

    return func() int {
        if i > 0 {
            i--
        }
        return i
    }
}

https://ithelp.ithome.com.tw/upload/images/20230927/20162693j54cfR5kZU.png

以上就是本篇的整理~~


上一篇
[Day11] Go in 30 - 函式- 簡介
下一篇
[Day13] Go in 30 - 函式 - 以函式為型別的參數
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言