99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go語(yǔ)言 函數(shù)聲明和調(diào)用

2023-02-16 17:36 更新

除了上一篇文章介紹的運(yùn)算符操作,函數(shù)操作是另一種在編程中常用的操作。 函數(shù)操作常被稱為函數(shù)調(diào)用。此篇文章將介紹如何在Go中聲明和調(diào)用函數(shù)。

函數(shù)聲明

讓我們來(lái)看一個(gè)函數(shù)聲明:

func SquaresOfSumAndDiff(a int64, b int64) (s int64, d int64) {
	x, y := a + b, a - b
	s = x * x
	d = y * y
	return // <=> return s, d
}

從上面的例子中,我們可以發(fā)現(xiàn)一個(gè)函數(shù)聲明從左到右由以下部分組成:

  1. 第一部分是func關(guān)鍵字。
  2. 第二部分是函數(shù)名稱。函數(shù)名稱必須是一個(gè)標(biāo)識(shí)符。 這里的函數(shù)名稱是SquareOfSumAndDiff。
  3. 第三部分是輸入?yún)?shù)列表。輸入?yún)?shù)聲明列表必須用一對(duì)小括號(hào)括起來(lái)。 輸入?yún)?shù)聲明有時(shí)也稱為形參聲明(對(duì)應(yīng)后面將介紹的函數(shù)調(diào)用中的實(shí)參)。
  4. 第四部分是輸出結(jié)果聲明列表。在Go中,一個(gè)函數(shù)可以有多個(gè)返回值。 比如上面這個(gè)例子就有兩個(gè)返回值。 當(dāng)一個(gè)函數(shù)的輸出結(jié)果聲明列表為空或者只包含一個(gè)匿名結(jié)果聲明時(shí),此列表可以不用一對(duì)小括號(hào)括起來(lái)(見(jiàn)下面的示例);否則,小括號(hào)是必需的。
  5. 最后一部分是函數(shù)體。函數(shù)體必須用一對(duì)大括號(hào)括起來(lái)。 一對(duì)大括號(hào)和它其間的代碼形成了一個(gè)顯式代碼塊。 在一個(gè)函數(shù)體內(nèi),return關(guān)鍵字可以用來(lái)結(jié)束此函數(shù)的正常向前執(zhí)行流程并進(jìn)入此函數(shù)的退出階段(詳見(jiàn)下下節(jié)中的解釋)。

在上面的例子中,每個(gè)函數(shù)參數(shù)和結(jié)果聲明都由一個(gè)名字和一個(gè)類型組成(變量名字在前,類型在后)。 我們可以把一個(gè)參數(shù)和結(jié)果聲明看作是一個(gè)省略了var關(guān)鍵字的標(biāo)準(zhǔn)變量聲明。 上面這個(gè)函數(shù)有兩個(gè)輸入?yún)?shù)(ab)以及兩個(gè)輸出結(jié)果(xy)。 它們的類型都是int64

輸出結(jié)果聲明列表中的所有聲明中的結(jié)果名稱可以(而且必須)同時(shí)出現(xiàn)或者同時(shí)省略。 這兩種方式在實(shí)踐中都使用得很廣泛。 如果一個(gè)返回結(jié)果聲明中的結(jié)果名稱沒(méi)有省略,則這個(gè)返回結(jié)果稱為具名返回結(jié)果。否則稱為匿名返回結(jié)果。

如果一個(gè)函數(shù)聲明的所有返回結(jié)果均為匿名的,則在此函數(shù)體內(nèi)的返回語(yǔ)句return關(guān)鍵字后必須跟隨一系列返回值,這些返回值和此函數(shù)的各個(gè)返回結(jié)果聲明一一對(duì)應(yīng)。比如,下面這個(gè)函數(shù)聲明和上例中的函數(shù)聲明是等價(jià)的。

func SquaresOfSumAndDiff(a int64, b int64) (int64, int64) {
	return (a+b) * (a+b), (a-b) * (a-b)
}

事實(shí)上,如果一個(gè)函數(shù)聲明中的所有輸入?yún)?shù)在此函數(shù)體內(nèi)都沒(méi)有被使用過(guò),則它們也可以都同時(shí)是匿名的。 不過(guò)這種情形在實(shí)際編程中很少見(jiàn)。

盡管一個(gè)函數(shù)聲明中的輸入?yún)?shù)和返回結(jié)果看上去是聲明在這個(gè)函數(shù)體的外部,但是在此函數(shù)體內(nèi),這些輸入?yún)?shù)和輸出結(jié)果被當(dāng)作局部變量來(lái)使用。 但輸入?yún)?shù)和輸出結(jié)果和普通局部變量還是有一點(diǎn)區(qū)別的:目前的主流Go編譯器不允許一個(gè)名稱不為_的普通局部變量被聲明而不有效使用。

Go不支持輸入?yún)?shù)默認(rèn)值。每個(gè)返回結(jié)果的默認(rèn)值是它的類型的零值。 比如,下面的函數(shù)在被調(diào)用時(shí)將打印出(和返回)0 false。

func f() (x int, y bool) {
	println(x, y) // 0 false
	return
}

和普通的變量聲明一樣,如果若干連續(xù)的輸入?yún)?shù)或者返回結(jié)果的類型相同,則在它們的聲明中可以共用一個(gè)類型。 比如,上面的兩個(gè)SquaresOfSumAndDiff函數(shù)聲明和下面這個(gè)是完全等價(jià)的。

func SquaresOfSumAndDiff(a, b int64) (s, d int64) {
	return (a+b) * (a+b), (a-b) * (a-b)
	// 上面這行等價(jià)于下面這行:
	// s = (a+b) * (a+b); d = (a-b) * (a-b); return
}

注意,盡管在上面這個(gè)函數(shù)聲明的返回結(jié)果都是具名的,函數(shù)體內(nèi)的return關(guān)鍵字后仍然可以跟返回值。

如果一個(gè)函數(shù)聲明只包含一個(gè)返回結(jié)果,并且此返回結(jié)果是匿名的,則此函數(shù)聲明中的返回結(jié)果部分不必用小括號(hào)括起來(lái)。 如果一個(gè)函數(shù)聲明的返回結(jié)果列表為空,則此函數(shù)聲明中的返回結(jié)果部分可以完全被省略掉。 一個(gè)函數(shù)聲明的輸入?yún)?shù)列表部分總不能省略掉,即使此函數(shù)聲明的輸入?yún)?shù)列表為空。

下面是更多函數(shù)聲明的例子:

func CompareLower4bits(m, n uint32) (r bool) {
	// 下面這兩行等價(jià)于:return m&0xFF > n&0xff
	r = m&0xF > n&0xf
	return
}

// 此函數(shù)沒(méi)有輸入?yún)?shù)。它的結(jié)果聲明列表只包含一個(gè)
// 匿名結(jié)果聲明,因此它不必用()括起來(lái)。
func VersionString() string {
	return "go1.0"
}

// 此函數(shù)沒(méi)有返回結(jié)果。它的所有輸入?yún)?shù)都是匿名的。
// 它的結(jié)果聲明列表為空,因此可以被省略掉。
func doNothing(string, int) {
}

在前面的《Go語(yǔ)言101》文章中,我們已經(jīng)知道一個(gè)程序的main入口函數(shù)必須不帶任何輸入?yún)?shù)和返回結(jié)果。

注意,在Go中,所有函數(shù)都必須直接聲明在包級(jí)代碼塊中。 或者說(shuō),任何一個(gè)函數(shù)都不能被聲明在另一個(gè)函數(shù)體內(nèi)。 雖然匿名函數(shù)(將在下面的某節(jié)中介紹)可以定義在函數(shù)體內(nèi),但匿名函數(shù)定義不屬于函數(shù)聲明。

函數(shù)調(diào)用

一個(gè)聲明的函數(shù)可以通過(guò)它的名稱和一個(gè)實(shí)參列表來(lái)調(diào)用之。 一個(gè)實(shí)參列表必須用小括號(hào)括起來(lái)。 實(shí)參列表中的每一個(gè)單值實(shí)參對(duì)應(yīng)著(或稱被傳遞給了)一個(gè)形參。

注意:函數(shù)傳參也屬于賦值操作。在傳參中,各個(gè)實(shí)參被賦值給各個(gè)對(duì)應(yīng)形參。

一個(gè)實(shí)參值的類型不必一定要和其對(duì)應(yīng)的形參聲明的類型一樣。 但如果一個(gè)實(shí)參值的類型和其對(duì)應(yīng)的形參聲明的類型不一致,則此實(shí)參必須能夠隱式轉(zhuǎn)換到其對(duì)應(yīng)的形參的類型。

如果一個(gè)函數(shù)帶有返回值,則它的一個(gè)調(diào)用被視為一個(gè)表達(dá)式。如果此函數(shù)返回多個(gè)結(jié)果,則它的每個(gè)調(diào)用被視為一個(gè)多值表達(dá)式。 一個(gè)多值表達(dá)式可以被同時(shí)賦值給多個(gè)目標(biāo)值(數(shù)量必須匹配,各個(gè)輸出結(jié)果被賦值給相對(duì)應(yīng)的目標(biāo)值)。

下面這個(gè)例子完整地展示了如何調(diào)用幾個(gè)已經(jīng)聲明了的函數(shù)。

package main

func SquaresOfSumAndDiff(a int64, b int64) (int64, int64) {
	return (a+b) * (a+b), (a-b) * (a-b)
}

func CompareLower4bits(m, n uint32) (r bool) {
	r = m&0xF > n&0xf
	return
}

// 使用一個(gè)函數(shù)調(diào)用的返回結(jié)果來(lái)初始化一個(gè)包級(jí)變量。
var v = VersionString()

func main() {
	println(v) // v1.0
	x, y := SquaresOfSumAndDiff(3, 6)
	println(x, y) // 81 9
	b := CompareLower4bits(uint32(x), uint32(y))
	println(b) // false
	// "Go"的類型被推斷為string;1的類型被推斷為int32。
	doNothing("Go", 1)
}

func VersionString() string {
	return "v1.0"
}

func doNothing(string, int32) {
}

從上例可以看出,一個(gè)函數(shù)的聲明可以出現(xiàn)在它的調(diào)用之前,也可以出現(xiàn)在它的調(diào)用之后。

一個(gè)函數(shù)調(diào)用可以被延遲執(zhí)行或者在另一個(gè)協(xié)程(goroutine,或稱綠色線程)中執(zhí)行。 后面的一文將對(duì)這兩個(gè)特性進(jìn)行詳解。

函數(shù)調(diào)用的退出階段

在Go中,當(dāng)一個(gè)函數(shù)調(diào)用返回后(比如執(zhí)行了一個(gè)return語(yǔ)句或者函數(shù)中的最后一條語(yǔ)句執(zhí)行完畢), 此調(diào)用可能并未立即退出。一個(gè)函數(shù)調(diào)用從返回開(kāi)始到最終退出的階段稱為此函數(shù)調(diào)用的退出階段(exiting phase)。 函數(shù)調(diào)用的退出階段的意義將在講解延遲函數(shù)的時(shí)候體現(xiàn)出來(lái)。

函數(shù)調(diào)用的退出階段將在后面的一篇文章中詳細(xì)解釋。

匿名函數(shù)

Go支持匿名函數(shù)。定義一個(gè)匿名函數(shù)和聲明一個(gè)函數(shù)類似,但是一個(gè)匿名函數(shù)的定義中不包含函數(shù)名稱部分。 注意匿名函數(shù)定義不是一個(gè)函數(shù)聲明。

一個(gè)匿名函數(shù)在定義后可以被立即調(diào)用,比如:

package main

func main() {
	// 這個(gè)匿名函數(shù)沒(méi)有輸入?yún)?shù),但有兩個(gè)返回結(jié)果。
	x, y := func() (int, int) {
		println("This function has no parameters.")
		return 3, 4
	}() // 一對(duì)小括號(hào)表示立即調(diào)用此函數(shù)。不需傳遞實(shí)參。

	// 下面這些匿名函數(shù)沒(méi)有返回結(jié)果。

	func(a, b int) {
		println("a*a + b*b =", a*a + b*b) // a*a + b*b = 25
	}(x, y) // 立即調(diào)用并傳遞兩個(gè)實(shí)參。

	func(x int) {
		// 形參x遮擋了外層聲明的變量x。
		println("x*x + y*y =", x*x + y*y) // x*x + y*y = 32
	}(y) // 將實(shí)參y傳遞給形參x。

	func() {
		println("x*x + y*y =", x*x + y*y) // x*x + y*y = 25
	}() // 不需傳遞實(shí)參。
}

注意,上例中的最后一個(gè)匿名函數(shù)處于變量xy的作用域內(nèi),所以在它的函數(shù)體內(nèi)可以直接使用這兩個(gè)變量。 這樣的函數(shù)稱為閉包(closure)。事實(shí)上,Go中的所有的自定義函數(shù)(包括聲明的函數(shù)和匿名函數(shù))都可以被視為閉包。 這就是為什么Go中的函數(shù)使用起來(lái)和動(dòng)態(tài)語(yǔ)言中的函數(shù)一樣靈活。

在后面的文章中,我們將了解到一個(gè)匿名函數(shù)可以被賦值給某個(gè)函數(shù)類型的值,從而我們不必在定義完此匿名函數(shù)后立即調(diào)用它,而是可以在以后合適的時(shí)候再調(diào)用它。

內(nèi)置函數(shù)

Go支持一些內(nèi)置函數(shù),比如前面的例子中已經(jīng)用到過(guò)多次的printlnprint函數(shù)。 我們可以不引入任何庫(kù)包(見(jiàn)下一篇文章)而調(diào)用一個(gè)內(nèi)置函數(shù)。

我們可以使用內(nèi)置函數(shù)realimag來(lái)得到一個(gè)復(fù)數(shù)的實(shí)部和虛部(均為浮點(diǎn)數(shù)類型)。 注意,如果這兩個(gè)函數(shù)的任何一個(gè)調(diào)用的實(shí)參是一個(gè)常量,則此調(diào)用將在編譯時(shí)刻被估值,其返回結(jié)果也是一個(gè)常量。 此調(diào)用將被視為一個(gè)常量表達(dá)式。特別地,如果此實(shí)參是一個(gè)類型不確定值,則返回結(jié)果也是一個(gè)類型不確定值。

一個(gè)例子:

// c是一個(gè)類型不確定復(fù)數(shù)常量。
const c = complex(1.6, 3.3)

// 函數(shù)調(diào)用real(c)和imag(c)的結(jié)果都是類型
// 不確定浮點(diǎn)數(shù)值。在下面這句賦值中,它們都
// 被推斷為float32類型的值。
var a, b float32 = real(c), imag(c)

// 變量d的類型被推斷為內(nèi)置類型complex64。
// 函數(shù)調(diào)用real(d)和imag(d)的結(jié)果都是
// 類型為float32的類型確定值。
var d = complex(a, b)

// 變量e的類型被推斷為內(nèi)置類型complex128。
// 函數(shù)調(diào)用real(e)和imag(e)的結(jié)果都是
// 類型為float64的類型確定值。
var e = c

更多內(nèi)置類型將在很多后面其它文章中介紹。

更多函數(shù)相關(guān)的概念

本文是一篇Go函數(shù)入門的文章,很多其它函數(shù)相關(guān)的概念并未在此文中解釋。 今后,我們可以從函數(shù)類型和函數(shù)值一文中了解到和函數(shù)相關(guān)的其它概念。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)