函数是基于性能或者逻辑进行聚合的可复用的代码块。将一些简单的、简短的代码抽离封装成多个代码片段,即函数,有助于进步代码逻辑的可读性和可维护性。不同于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中的一种数据类型,能够作为另一个函数的参数,也能够作为另一个函数的返回值,应用起来相当灵便,但咱们也不能矫枉过正,毫无节制地用函数封装逻辑,造成适度封装的景象。