函數是基於功能或者邏輯進行聚合的可復用的代碼塊。將一些複雜的、冗長的代碼抽離封裝成多個代碼片段,即函數,有助於提高代碼邏輯的可讀性和可維護性。不同於Python,由於 Go lang是編譯型語言,編譯之後再運行,所以函數的定義順序無關痛癢。 函數聲明 在 Go lang里,函數聲明語法如下: fun ...
函數是基於功能或者邏輯進行聚合的可復用的代碼塊。將一些複雜的、冗長的代碼抽離封裝成多個代碼片段,即函數,有助於提高代碼邏輯的可讀性和可維護性。不同於Python,由於 Go lang是編譯型語言,編譯之後再運行,所以函數的定義順序無關痛癢。
函數聲明
在 Go lang里,函數聲明語法如下:
func function_name(parameter_list) (result_list) {
//函數邏輯
}
這裡使用function的簡寫形式 func關鍵詞,後面依次接 function_name(函數名) , parameter_list(參數列表) , result_list(返回值列表)以及函數體 。
parameter_list(參數列表)成員:函數的參數名以及參數類型,這些參數作為局部變數,其值由參數調用者提供,函數中的參數列表和返回值並非是必須的。
result_list(返回值列表):函數返回值的變數名以及類型,如果函數返回一個無名變數或者沒有返回值,返回值列表的括弧是可以省略的。
如果有連續若幹個參數的類型一致,那麼只需在最後一個參數後添加該類型:
package main
import "fmt"
// 函數返回一個無名變數,返回值列表的括弧省略
func sum(x int, y int) int {
return x + y
}
// 無參數列表和返回值
func printBookName() {
fmt.Println("go lang1.18")
}
// 參數的類型一致,只在最後一個參數後添加該類型
func sub(x, y int) int {
return x - y
}
func main() {
fmt.Println("1 + 1 = ", sum(1, 1))
printBookName()
fmt.Println("2 - 1 =", sub(2, 1))
}
程式返回:
command-line-arguments
1 + 1 = 2
go lang1.18
2 - 1 = 1
不定長參數
和Python一樣,Go lang也支持不定長參數,即參數有多少個並不確定的情況。
在參數類型前面加 ... 表示一個切片,用來接收調用者傳入的參數。註意,如果該函數下有其他類型的參數,這些其他參數必須放在參數列表的前面,切片必須放在最後:
package main
import "fmt"
func show(args ...string) int {
sum := 0
for _, item := range args {
fmt.Println(item)
sum += 1
}
return sum
}
func main() {
fmt.Println(show("1", "2", "3"))
}
和Python的*args用法差不多,但需要註意必須要聲明函數的數據類型,程式返回:
1
2
3
3
如果傳多個參數的數據類型都不一樣,可以指定類型為 ...interface{} ,然後再進行遍歷:
package main
import "fmt"
func PrintType(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "type is int.")
case string:
fmt.Println(arg, "type is string.")
case float64:
fmt.Println(arg, "type is float64.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
PrintType(1, 3.1415, "go lang 1.18")
}
此外,還可以使用 ... 可以用來解序列,能將函數的可變參數(即切片)一個一個取出來,傳遞給另一個可變參數的函數,而不是傳遞可變參數變數本身:
package main
import "fmt"
func main() {
var s []string
s = append(s, []string{"1", "2", "3"}...)
fmt.Println(s)
}
這裡將字元串切片取出來後,傳遞給內置的append方法,程式返回:
[1 2 3]
函數的返回值
一個函數可以沒有返回值,也可以有一個返回值,也可以有返回多個值:
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A + B
Multiplied = A * B
return
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
fmt.Println(SumAndProduct(1, 2))
}
程式返回:
Kumar Mahesh
3 2
_ 是Go lang里的空白標識符。它可以代替任何類型的任何值。我們可以利用它來忽略某些別人會用到但我們不會用到的函數返回值:
package main
import (
"fmt"
)
func rectProps(length, width float64) (float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, _ := rectProps(10.8, 5.6) // perimeter is discarded
fmt.Printf("Area %f ", area)
}
程式返回:
Area 60.480000
匿名函數
有點類似Python中的lambda表達式,但實際上並不是作為語法糖而存在:
package main
import (
"fmt"
)
func main() {
f := func() {
fmt.Println("hello world")
}
f() //hello world
fmt.Printf("%T\n", f) //列印 func()
}
程式返回:
hello world
func()
一望而知,只是匿名而已,但通過變數可調用,另外也可以擁有參數:
package main
import (
"fmt"
)
func main() {
f:=func(args string){
fmt.Println(args)
}
f("hello world")//hello world
//或
(func(args string){
fmt.Println(args)
})("hello world")//hello world
//或
func(args string) {
fmt.Println(args)
}("hello world") //hello world
}
程式返回:
hello world
hello world
hello world
基本上,匿名函數和命名函數用法上並無二致。
閉包(closure)
很多語言都有閉包的概念,簡單理解就是函數的嵌套:
package main
import "fmt"
func main() {
a := Fun()
b:=a("hello ")
c:=a("hello ")
fmt.Println(b)//worldhello
fmt.Println(c)//worldhello hello
}
func Fun() func(string) string {
a := "world"
return func(args string) string {
a += args
return a
}
}
程式返回:
worldhello
worldhello hello
這裡我們將方法作為參數傳遞到方法內部執行,這樣內層的函數可以使用外層函數的所有變數,即使外層函數已經執行完畢。
延遲函數
延遲其實是延遲(defer)語句,延遲語句被用於執行一個函數調用,在這個函數之前,延遲語句返回:
package main
import "fmt"
func main() {
a := 1
b := 2
defer fmt.Println(b)
fmt.Println(a)
}
程式返回:
1
2
說白了就是一種倒裝的形式,非延遲語句先執行,最後再執行延遲語句。
延遲也並不僅僅局限於函數內部語句,延遲一個方法調用也是可以的:
package main
import (
"fmt"
)
type person struct {
firstName string
lastName string
}
func (p person) fullName() {
fmt.Printf("%s %s", p.firstName, p.lastName)
}
func main() {
p := person{
firstName: "go lang",
lastName: "python",
}
defer p.fullName()
fmt.Printf("Welcome ")
}
程式返回:
Welcome go lang python
初始化函數
顧名思義,和Python中的魔法方法init一樣,可以提前做一些初始化操作:
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
這裡的初始化順序是:變數初始化->init()->main()
和Python不同的是,每個包可以有多個初始化函數。
結語
歸根結底,函數可以被認為是Go lang中的一種數據類型,可以作為另一個函數的參數,也可以作為另一個函數的返回值,使用起來相當靈活,但我們也不能矯枉過正,毫無節制地用函數封裝邏輯,造成過度封裝的現象。