化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的 ...

打印 上一主题 下一主题

主题 891|帖子 891|积分 2673

函数是基于功能或者逻辑进行聚合的可复用的代码块。将一些复杂的、冗长的代码抽离封装成多个代码片段,即函数,有助于提高代码逻辑的可读性和可维护性。不同于Python,由于 Go lang是编译型语言,编译之后再运行,所以函数的定义顺序无关痛痒。
函数声明

在 Go lang里,函数声明语法如下:
  1. func function_name(parameter_list) (result_list) {  
  2.     //函数逻辑  
  3. }
复制代码
这里使用function的简写形式 func关键词,后面依次接 function_name(函数名) , parameter_list(参数列表) , result_list(返回值列表)以及函数体 。
parameter_list(参数列表)成员:函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供,函数中的参数列表和返回值并非是必须的。
result_list(返回值列表):函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。
如果有连续若干个参数的类型一致,那么只需在最后一个参数后添加该类型:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. // 函数返回一个无名变量,返回值列表的括号省略  
  6. func sum(x int, y int) int {  
  7.         return x + y  
  8. }  
  9.   
  10. // 无参数列表和返回值  
  11. func printBookName() {  
  12.         fmt.Println("go lang1.18")  
  13. }  
  14.   
  15. // 参数的类型一致,只在最后一个参数后添加该类型  
  16. func sub(x, y int) int {  
  17.         return x - y  
  18. }  
  19.   
  20. func main() {  
  21.         fmt.Println("1 + 1 = ", sum(1, 1))  
  22.         printBookName()  
  23.         fmt.Println("2 - 1 =", sub(2, 1))  
  24. }
复制代码
程序返回:
  1. command-line-arguments  
  2. 1 + 1 =  2  
  3. go lang1.18  
  4. 2 - 1 = 1
复制代码
不定长参数

和Python一样,Go lang也支持不定长参数,即参数有多少个并不确定的情况。
在参数类型前面加 ... 表示一个切片,用来接收调用者传入的参数。注意,如果该函数下有其他类型的参数,这些其他参数必须放在参数列表的前面,切片必须放在最后:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func show(args ...string) int {  
  6.         sum := 0  
  7.         for _, item := range args {  
  8.                 fmt.Println(item)  
  9.                 sum += 1  
  10.         }  
  11.         return sum  
  12. }  
  13.   
  14. func main() {  
  15.         fmt.Println(show("1", "2", "3"))  
  16. }
复制代码
和Python的*args用法差不多,但需要注意必须要声明函数的数据类型,程序返回:
  1. 1  
  2. 2  
  3. 3  
  4. 3
复制代码
如果传多个参数的数据类型都不一样,可以指定类型为 ...interface{} ,然后再进行遍历:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func PrintType(args ...interface{}) {  
  6.         for _, arg := range args {  
  7.                 switch arg.(type) {  
  8.                 case int:  
  9.                         fmt.Println(arg, "type is int.")  
  10.                 case string:  
  11.                         fmt.Println(arg, "type is string.")  
  12.                 case float64:  
  13.                         fmt.Println(arg, "type is float64.")  
  14.                 default:  
  15.                         fmt.Println(arg, "is an unknown type.")  
  16.                 }  
  17.         }  
  18. }  
  19.   
  20. func main() {  
  21.         PrintType(1, 3.1415, "go lang 1.18")  
  22. }
复制代码
此外,还可以使用 ... 可以用来解序列,能将函数的可变参数(即切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func main() {  
  6.         var s []string  
  7.         s = append(s, []string{"1", "2", "3"}...)  
  8.         fmt.Println(s)  
  9. }
复制代码
这里将字符串切片取出来后,传递给内置的append方法,程序返回:
  1. [1 2 3]
复制代码
函数的返回值

一个函数可以没有返回值,也可以有一个返回值,也可以有返回多个值:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func swap(x, y string) (string, string) {  
  6.         return y, x  
  7. }  
  8.   
  9. func SumAndProduct(A, B int) (add int, Multiplied int) {  
  10.         add = A + B  
  11.         Multiplied = A * B  
  12.         return  
  13. }  
  14.   
  15. func main() {  
  16.         a, b := swap("Mahesh", "Kumar")  
  17.         fmt.Println(a, b)  
  18.   
  19.         fmt.Println(SumAndProduct(1, 2))  
  20.   
  21. }
复制代码
程序返回:
  1. Kumar Mahesh  
  2. 3 2
复制代码
_ 是Go lang里的空白标识符。它可以代替任何类型的任何值。我们可以利用它来忽略某些别人会用到但我们不会用到的函数返回值:
  1. package main  
  2.   
  3. import (  
  4.         "fmt"  
  5. )  
  6.   
  7. func rectProps(length, width float64) (float64, float64) {  
  8.         var area = length * width  
  9.         var perimeter = (length + width) * 2  
  10.         return area, perimeter  
  11. }  
  12. func main() {  
  13.         area, _ := rectProps(10.8, 5.6) // perimeter is discarded  
  14.         fmt.Printf("Area %f ", area)  
  15. }
复制代码
程序返回:
  1. Area 60.480000
复制代码
匿名函数

有点类似Python中的lambda表达式,但实际上并不是作为语法糖而存在:
  1. package main  
  2.   
  3. import (  
  4.         "fmt"  
  5. )  
  6.   
  7. func main() {  
  8.         f := func() {  
  9.                 fmt.Println("hello world")  
  10.         }  
  11.         f()                   //hello world  
  12.         fmt.Printf("%T\n", f) //打印 func()  
  13. }
复制代码
程序返回:
  1. hello world  
  2. func()
复制代码
一望而知,只是匿名而已,但通过变量可调用,另外也可以拥有参数:
  1. package main  
  2.   
  3. import (  
  4.    "fmt"  
  5. )  
  6. func main() {  
  7.    f:=func(args string){  
  8.       fmt.Println(args)  
  9.    }  
  10.    f("hello world")//hello world  
  11.    //或  
  12.    (func(args string){  
  13.         fmt.Println(args)  
  14.     })("hello world")//hello world  
  15.     //或  
  16.     func(args string) {  
  17.         fmt.Println(args)  
  18.     }("hello world") //hello world  
  19. }
复制代码
程序返回:
  1. hello world  
  2. hello world  
  3. hello world
复制代码
基本上,匿名函数和命名函数用法上并无二致。
闭包(closure)

很多语言都有闭包的概念,简单理解就是函数的嵌套:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func main() {  
  6.     a := Fun()  
  7.     b:=a("hello ")  
  8.     c:=a("hello ")  
  9.     fmt.Println(b)//worldhello   
  10.     fmt.Println(c)//worldhello hello   
  11. }  
  12. func Fun() func(string) string {  
  13.     a := "world"  
  14.     return func(args string) string {  
  15.         a += args  
  16.         return  a  
  17.     }  
  18. }
复制代码
程序返回:
  1. worldhello   
  2. worldhello hello
复制代码
这里我们将方法作为参数传递到方法内部执行,这样内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。
延迟函数

延迟其实是延迟(defer)语句,延迟语句被用于执行一个函数调用,在这个函数之前,延迟语句返回:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. func main() {  
  6.         a := 1  
  7.         b := 2  
  8.         defer fmt.Println(b)  
  9.         fmt.Println(a)  
  10. }
复制代码
程序返回:
  1. 1  
  2. 2
复制代码
说白了就是一种倒装的形式,非延迟语句先执行,最后再执行延迟语句。
延迟也并不仅仅局限于函数内部语句,延迟一个方法调用也是可以的:
  1. package main  
  2.   
  3. import (  
  4.         "fmt"  
  5. )  
  6.   
  7. type person struct {  
  8.         firstName string  
  9.         lastName  string  
  10. }  
  11.   
  12. func (p person) fullName() {  
  13.         fmt.Printf("%s %s", p.firstName, p.lastName)  
  14. }  
  15.   
  16. func main() {  
  17.         p := person{  
  18.                 firstName: "go lang",  
  19.                 lastName:  "python",  
  20.         }  
  21.         defer p.fullName()  
  22.         fmt.Printf("Welcome ")  
  23. }
复制代码
程序返回:
  1. Welcome go lang python
复制代码
初始化函数

顾名思义,和Python中的魔法方法init一样,可以提前做一些初始化操作:
  1. package main  
  2.   
  3. import "fmt"  
  4.   
  5. var a int = initVar()  
  6.   
  7. func init() {  
  8.         fmt.Println("init2")  
  9. }  
  10.   
  11. func init() {  
  12.         fmt.Println("init")  
  13. }  
  14.   
  15. func initVar() int {  
  16.         fmt.Println("init var...")  
  17.         return 100  
  18. }  
  19.   
  20. func main() {  
  21.         fmt.Println("main...")  
  22. }
复制代码
程序返回:
  1. init var...  
  2. init2  
  3. init
复制代码
这里的初始化顺序是:变量初始化->init()->main()
和Python不同的是,每个包可以有多个初始化函数。
结语

归根结底,函数可以被认为是Go lang中的一种数据类型,可以作为另一个函数的参数,也可以作为另一个函数的返回值,使用起来相当灵活,但我们也不能矫枉过正,毫无节制地用函数封装逻辑,造成过度封装的现象。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美丽的神话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表