• Go语言函数相关


     

    1.函数的声明定义

    //func关键字
    
    //getStudent函数名
    
    //(id int, classId int) 参数列表
    
    //(name string,age int) 返回值列表
    
    func getStudent(id int, classId int)(name string,age int) {
    
       //函数体
    
      if id==1&&classId==1{
     
         name = "BigOrange"
    
         age = 26
    
       }
    
      //返回值
      
      return name, age // 支持多重返回值
    
    }

    有意思的是Go语言的返回值可以有多个,并且放在了参数列表后面,而C#等都是在函数名之前,也没有关键字。

    2.函数的调用

    import "fmt"
    
    //调用fmt包中的Println方法。
    
    fmt.Println("Name:", std.Name, "Age:",std.Age)

    3.函数编写的原则

    很好奇为什么没有public private等关键字,那函数怎么才能定义为公用和私有呢?

    Go语言有这样的规则:小写字母开头的函数只在本包内可见,大写字母开头的函数才

    能被其他包使用。这个规则也适用于类型和变量的可见性。

     

    4.不定参数问题

    不定参数是指函数传入的参数个数为不定数量。

    func myfunc(args ...int) {
         for _, arg := range args {
         fmt.Println(arg)
      }
    }

    函数myfunc()接受不定数量的参数,这些参数的类型全部是int

    ※形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。

    它是一个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。

    从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为

    什么上面的参数args可以用for循环来获得每个传入的参数。

     

    不定参数的传递

    myfunc3(args ...int)

    对应上面的这个函数,传递参数可以为下面两种

    // 按原样传递
    myfunc3(args...)
    // 传递片段,实际上任意的int slice都可以传进去
    myfunc3(args[1:]...)

    任意类型的不定参数

    可以看到 fmt.Println()方法接受了不定参数,但它是 ...interface{}

    用interface{}传递任意类型数据是Go语言的惯例用法。

     

    5.多返回值

    Go语言函数可以返回多个返回值

    如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单

    地用一个下划线“_”来跳过这个返回值

     

    6.匿名函数

    Go语言支持随时在代码里定义匿名函数。

    匿名函数由一个不带函数名的函数声明和函数体组成

    func(a, b int, z float64) bool {
         return a*b <int(z)
    }

    匿名函数可以直接赋值给一个变量或者直接执行:(有点像js哈)

    f := func(x, y int) int {
         return x + y
    }
    func(ch chan int) {
        ch <- ACK
    } (reply_chan) // 花括号后直接跟参数列表表示函数调用

    7.闭包

     

    
    
    package main
    import (
    "fmt"
    )
    func main() {
    var j int = 5
    a := func()(func()) {
    var i int = 10
    return func() {
    fmt.Printf("i, j: %d, %d ", i, j)
    }
    }()
    a()
    j *= 2
    a()
    }

    结果:

    i, j: 10, 5
    i, j: 10, 10

    分析:

    1---"...func()(func()) {....." 

           表明此匿名函数返回值的类型是func(), 即此匿名函数返回一个函数指针(此处引用一下c 的概念);

    2---"...return func() { 

                fmt.Printf("i, j: %d, %d ", i, j) 

            }..." 

            表明返回的函数指针指向一个打印 i, j: %d, %d   的函数;

    3---"...a := func()(func()) {

             ...

             }()..."

             末尾的括号表明匿名函数被调用,并将返回的函数指针赋给变量a ;

    综合来看:

    "...a := func()(func()) { 

            var i int = 10 

            return func() { 

                fmt.Printf("i, j: %d, %d ", i, j) 

            } 

        }()..."

    此代码片段的意思"等价于"

    a := func() { 

                fmt.Printf("i, j: %d, %d ", i, j) 

            }

    至于为何要用匿名函数如此的转一圈,是因为要引用闭包的概念,此概念省略不表,多写点代码试试就能体会了。

     

    补充:传值和传引用

     

    2018/08/22 补充

    1.type 定义一个类型,有两种方式

    ①配合struct,创建一个新的结构,类似C#里面的Class

    ②配合既存的类型(int64...),创建一个新的类型,类似C++里面的 typedef

    2.Struct的如果不进行赋值,会使用0值进行初始化。

    3.type使用既存类型定义新结构,和既存类型不是同一个类型,不能进行转换,例如

     

    package main
    
    type Integer int64
    func main() {
    var srcInt Integer srcInt = int64(1000) }

    结果:

    4.方法和函数的区别

    方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字func 和方法名之间增加了一个参数

    例如:

    这是函数,它的调用就是直接在使用的时候 传入参数获取返回值就行

    func getStudentName(student Student)(name string) {
        //返回值
        return student.Name 
    }

    这是方法

    package main
    
    import (
        "fmt"
    )
    
    type Student struct
    {   
        Name string
        Age int
    }
    
    //Student类的方法 使用值接收者实现了一个方法
    func (student Student) getStudentName()(string){
        return student.Name
    }
    
    //Student类的方法 使用指针接收者实现了一个方法
    func (student *Student) changeStudentName(name string){
        student.Name = name
        fmt.Println("方法执行之后name",student.Name)
    
    }
    
    //Student类的方法 使用指针接收者实现了一个方法
    func (student Student) changeStudentNameByValue(name string){
        student.Name = name
        fmt.Println("方法执行之后name",student.Name)
    }
    
    func main() {
        bigOrange:=Student{
            Name:"BigOrange",Age:18,
        }
    
        bigApple:=Student{
            Name:"BigApple",Age:20,
        }
    
        //使用函数获取学生名称
        name1 := getStudentName(bigOrange)
        name2 := getStudentName(bigApple)
        
        fmt.Println("========通过传地址ChangeName之后==========")
        fmt.Println("方法执行之前name",name1)
        bigOrange.changeStudentName("BigBanana")
        name1 = bigOrange.getStudentName()
        fmt.Println("方法返回之后Name",name1)
    
        fmt.Println("========通过传值ChangeName之后===========")
        fmt.Println("方法执行之前name",name2)
        bigApple.changeStudentNameByValue("BigPear")
        name2 = bigApple.getStudentName()
        fmt.Println("方法返回之后Name",name2)
    }

    结果:

    ========通过传地址ChangeName之后==========
    方法执行之前name BigOrange
    方法执行之后name BigBanana
    方法返回之后Name BigBanana
    ========通过传值ChangeName之后===========
    方法执行之前name BigApple
    方法执行之后name BigPear
    方法返回之后Name BigApple

    上面的例子中

    分别使用了函数和方法进行获取学生姓名

    分别使用了传值和传地址的方式对学生名称进行修正

    综上

    ① Go语言中的方法,相比于函数,多了一个接收者参数

    ② Go 语言里有两种类型的接收者:值接收者和指针接收者

    方法如果使用 值接收者,那么调用者可以是值接收者类型、也可以是它的指针类型,请看下面的例子(补充上例):

    fmt.Println("========使用指针来调用值类型声明的接收者方法===========")
    bigGrape := &Student{ Name:"bigGrape",Age:22} //取地址!!!!
    name3 := bigGrape.getStudentName();
    fmt.Println("========通过传值ChangeName之后===========")
    fmt.Println("方法执行之前name",name3)
    bigGrape.changeStudentNameByValue("BigXXXX")
    name3 = bigGrape.getStudentName()
    fmt.Println("方法返回之后Name",name3)

    结果:

    ========使用指针来调用值类型声明的接收者方法===========
    name bigGrape
    ========通过传值ChangeName之后===========
    方法执行之前name bigGrape
    方法执行之后name BigXXXX
    方法返回之后Name bigGrape

    如上代码 使用了&获取地址,所以bigGrape是一个student类型的指针。下面的代码和上例一样,直接使用了【变量.方法】的方式调用方法,结果也和值类型调用的一样。

    为什么?

    【Go 在代码背后的执行动作】

    name4:=(*bigGrape).getStudentName()  

    Go 编译器为了支持这种方法调用背后做的事情。【指针被解引用为值】,这样就符合了值接收者的要求。再强调一次,getStudentName 操作的是一个副本,只不过这次操作的是从bigGrape指针指向的值的副本。

    同理还记得上面的,这句代码吗?对bigOrange这个变量修改了名称

    bigOrange.changeStudentName("BigBanana")  

    bigOrange和bigApple明显是值类型,但是 changeStudentName 接收者是一个指针类型,为什么能调用,也是基于Go代码背后的执行动作,将值类型取了地址。

    (&bigOrange).changeStudentName("BigOrange")

    所以对于Go语言调用方法的类型,使用值或者指针都是可以的,不用拘泥于类型。

     

  • 相关阅读:
    opencv源码编译安装后使用时出现undefined reference cv::imwrite
    OPPO手机永久打开USB调试模式
    bash 顺序执行等待前一个脚本执行完成
    cpp
    多线程
    关于nvme下ubuntu无法识别硬盘的问题
    极限建站
    新生赛
    pc_mbed_fpga_communication
    color_sensor_mbed
  • 原文地址:https://www.cnblogs.com/dcz2015/p/10096374.html
Copyright © 2020-2023  润新知