go - 函数

chuangrain · · 3288 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

函数
1. 格式
  func funcName(形参1 type[, 形参2 type...]) [([[返回变量1] type[, [返回变量2] type...]])] {
    [return [返回变量1[, 返回变量2...]]]
  }
  a. 如果形参类型都一样,可以这样写: 形参1, 形参2 type, 同时返回变量也一样
  b. 如果只有一个返回值或者无返回值, 可以去掉对应的()
  c. 如果返回有返回值,该函数中最外层要有return语句
  d. 返回语句(), 返回变量名可以省略
  e. []表示可省略
  f. 不支持默认参数
example code list
import "fmt"

func main() {
  //1. 函数的调用,多个返回值
  var (
    a = 10
    b = 100
  )
  a2, b2 := add(a, b)
  fmt.Println("a2, b2 = ", a2, ", ", b2) // a2, b2 = 20, 200
  //fmt.Println("a2, b2 = ", add(a, b)) //compile error: multiple-value add() in single-value context
}

func add(n1, n2 int) (res1, res2 int) {
    res1, res2 = n1*2, n2*2//不需要用 ":=" 声明, 如果写为res1, res2 := n1*2, n2*2会出错
    return
}

/*
//下面的add也可以为上面的写法
func add(n1, n2 int) (res1, res2 int) {
  return n1*2, n2*2
}
*/

2. 调用其他包中的函数
  先引入包,再调用:
  包名.函数()
  go语言中规则:小写字母开头的函数(变量/类型)只在本包中可见,大写字母开关的可以在其他包中被调用
  如果把数字转换为字符串,用strconv.itoa()  b := 66 strconv.Itoa(b) = "66"
  如果用string(b)结果为 "B"
example code list
import (
  "fmt"
  "math"
  "strconv"
)

func main() {
  //2. 调用 math, strconv 包中的函数
  fmt.Printf("sqrt(%d) = %.2f\n", b, math.Sqrt(float64(b))) //sqrt(100) = 10.00
  c := 67
  fmt.Println("string(67) = ", string(c))                   //string(67) = C
  fmt.Println("strconv.Itoa(67) = ", strconv.Itoa(c))       //strconv.Itoa(67) = 67
}

3. 多个返回值
  go支持多个返回值
  如: for k, v := range array ...
  a. 如果不想要某个返回值,可以用 "_" 代替,如 for _, v := range array...,不能直接写为这样 for , v :=range array...,但可以省略 v(值), 写为 for k := range array...
  b. 如果普通函数返回n个值时,不能用少于n个变量去接收
  c. 不能直接打印有多个返回值的函数调用结果
  d. 如果定义了返回值的变量名,就可以省略 return 语句后面的返回列表

4. 变参
  在调用函数时,参数个数不固定
  4.1 参数类型一样
    func1(var1[, var2...])
    func func1(varName ...type) {
      //语句
    }
    ...type = []type,相当于 slice

  4.2 参数类型不一样
    func func1(var ...interface{})
    用...interface{}表示任意类型,在 switch 参数.(type) {...}可以判断/得到变量的类型
    或者
    func func1(varName1 type, varName2 ...type)
    比如第一个参数类型确定,后面参数个数不确定
    func func1(s string, args ...int)//调用func1("hello", 1) 或者 func1("hello", 1, 2, 3)等等
  4.3 变参函数中调用[其他]变参函数
    func func1(varName ...type) {
      func2(varName...)   //原样
      func3(varName[i:j]...) //切片
    }
example code list
import "fmt"

func main() {
  //3. 变参
  //3.1 同类型
  uncert(1, 3, 5)
  uncert(2, 4)
  //3.2 不同类型
  uncert2(10.01, "Hello")
  uncert3("hello", 1)
  uncert3("hello", 1, 2, 3)
}

func uncert(args ...int) {
  for k, v := range args {
    fmt.Printf("%d => %d\n", k, v)
  }
  /**
  * 结果为: 
  * uncert(1, 3, 5)   |   uncert(2, 4)
  * 0 => 1            |   0 => 2
  * 1 => 3            |   1 => 4
  * 2 => 5
  */
  for k := range args {
    fmt.Printf("%d\n", k)
  }
  /**
  * 结果为: 参数(slice)的下标
  * uncert(1, 3, 5)   |   uncert(2, 4)
  * 0                 |   0
  * 1                 |   1
  * 2
  */
  uncert2(args[1:2]) //切片
}

func uncert2(args ...interface{}) {
  for k, v := range args {
    fmt.Println(k, " => ", v)
  }
  /**
  * 结果为: 参数(slice)的下标
  * uncert2(args[1:2])
  * uncert2(3)   |   uncert2(4)
  * 0 => [3]     |   0 => [4]
  *
  * uncert2(10.01, "Hello")
  * 0 => 10.01
  * 1 => Hello
  */
}

func uncert3(s string, n ...int) {
  for _, v := range n {
    fmt.Println(s, v)
  }
  /**
  * 结果为: 
  * uncert3("hello", 1)
  * hello 1
  *
  * uncert3("hello", 1, 2, 3)
  * hello 1
  * hello 2
  * hello 3
  */
}

5. 值传递与引用(指针)传递
  在参数传递的过程中,都会把参数复制一份,只不过有可能是值,有可能为地址
  如果是传地址,则操作同一地址的值,传递的参数可能会发生改变
  如果是值类型变量,传地址用: 
  func1(&varName)
  func func1(varName *type) {
    //操作指针
    //*varName 代替原来的varName
  }
  用 "&" 取变量地址,用 "*" 通过指针访问变量
example code list
import "fmt"

func main() {
  //4. 传值/地址
  hi := "hello"
  changeString(hi)
  fmt.Println("changeString(hi) = ", hi)        //changeString(hi) =  hello
  changeStringByAddr(&hi)
  fmt.Println("changeStringByAddr(&hi) = ", hi) //changeStringByAddr(&hi) =  world
}

func changeString(str string) {
  str = "world"
}

func changeStringByAddr(str *string) {
  *str = "world"
}

6. 函数作为值/类型
  6.1 函数作为值

    varName := funcName
    varName() //就相当于调用funcName()
    func funcName() {...}
  6.2 函数作为类型
    type typeName func(参数) (返回列表)
  6.3 匿名函数/闭包
    varName := func() {...}
    varName() //调用匿名函数
    //匿名函数也可以作为一个返回值赋值给变量
    func func1(m int) func(int) int {
      return func2(n int) int {
        return m + n
      }
    }
    varName := func1(m) //func1(m)返回的结果就是func2函数
    varName(n)//就相当于下面这个函数
    func2(n int) int {
      return m + n//m在func1(m)已确定值
    }
example code list
import "fmt"

func main() {
  //5. 函数作为值/类型
  //匿名函数
  varFunc := testFunc
  varFunc()
  //闭包
  varFunc1 := closure(10)
  fmt.Println(varFunc1(20)); //30
  /**************************/
  /*运行结果:               */
  /*func as a var           */
  /*30                      */
  /**************************/
}

func testFunc() {
  fmt.Println("func as a var")
}

func closure(m int) func(int) int {
  return func(n int) int {
    return m + n
  }
}

7. defer
  defer声明的语句会存在类型于栈的结构中,先进后出,在函数快执行完毕(或者return)时执行,类型于析构函数
  格式 
  defer 表达式|函数
  常用于文件关闭等操作,还有重要的 recover 函数
example code list
import "fmt"

func main() {
  //6. defer
  testDefer()
  /**************************/
  /*运行结果:               */
  /*defer start             */
  /*defer end               */
  /*3                       */
  /*2                       */
  /*1                       */
  /**************************/
}

func testDefer() {
  fmt.Println("defer start")
  defer fmt.Println("1")
  defer fmt.Println("2")
  defer fmt.Println("3")
  fmt.Println("defer end")
}

8. panic 和 recover
  go语言中无异常处理,使用 panic 和 recover 处理
  panic 使程序进入 panic 状态,recover 使程序从 panic 状态恢复正常
  8.1 panic
    panic("message") //手动中断程序,抛出 panic,但是 defer 会正常执行,参数可以是任意类型的
    注:如果程序出错,也可能会出现 panic
  8.2 recover
    recover 仅在 defer 函数中有效,无参数
    一般用法: 
    defer func() {
      if err := recover(); err != nil {
        //其他处理
      }
    }()
example code list
import "fmt"

func main() {
  //7. panic recover
  echoA()
  echoB()
  echoC()
  /**************************/
  /*运行结果:               */
  /*It is A                 */
  /*recover: It is B        */
  /*It is C                 */
  /**************************/
}

func echoA() {
  fmt.Println("It is A")
}

func echoB() {
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("recover: It is B")
    }
  }()
  panic("It is B") //注意顺序,要在 recover 后面
}

func echoC() {
  fmt.Println("It is C")
}
如果没有defer func() {...}结果如下所示: 


9. 补map交换key与value
  map中元素没有顺序
  example code list
import "fmt"

func main() {
  //8. 补map交换key与value
  changeKeyVal()
  /***********************************************/
  /*运行结果: (无顺序)                           */
  /*map[3:c 2:b 1:a 4:d] map[c:3 b:2 d:4 a:1]    */
  /***********************************************/
}

//交换map中的键和值
//注意:无序
func changeKeyVal() {
  m1 := map[int]string{1:"a", 2:"b", 3:"c", 4:"d"}
  m2 := make(map[string]int)
  for k, v := range m1 {
    m2[v] = k
  }
  fmt.Println(m1, m2)
}


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:chuangrain

查看原文:go - 函数

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3288 次点击  
加入收藏 微博
上一篇:go解析XML
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传