关于golang:15-Go语言函数是一等公民

3次阅读

共计 3348 个字符,预计需要花费 9 分钟才能阅读完成。

本文视频地址

函数是日常工作中非常罕用的,无论是 C 语言,C++、Java、Python、JavaScript 都是必不可少的。

Go 语言函数的特点如下:
1 func 关键字结尾。
2 反对多返回值。
3 反对具名返回值。
4 反对递归调用。
5 反对同类型的可变参数。
6 反对 defer,让函数优雅的返回。

一 GO 语言中如何让函数变成 一等公民 的?

1 失常创立。规范库代码如下
// $GOROOT/src/fmt/print.go
func newPrinter() *pp {p := ppFree.Get().(*pp)
    p.panicking = false
    p.erroring = false
    p.wrapErrs = false
    p.fmt.init(&p.buf)
    return p
}
2 在函数内创立

在 hexdumpWords 函数内定义了一个匿名函数(被赋值给 p1)

// $GOROOT/src/runtime/print.go
func hexdumpWords(p, end uintptr, mark func(uintptr) byte) {p1 := func(x uintptr) {var buf [2 * sys.PtrSize]byte
                for i := len(buf) - 1; i >= 0; i-- {
                        if x&0xF < 10 {buf[i] = byte(x&0xF) + '0'
                        } else {buf[i] = byte(x&0xF) - 10 + 'a'
                        }
                        x >>= 4
                }
                gwrite(buf[:])
        }
    ... ...
}
3 作为类型
// $GOROOT/src/net/http/server.go
type HandlerFunc func(ResponseWriter, *Request)
4 作为入参
$GOROOT/src/time/sleep.go

func AfterFunc(d Duration, f func()) *Timer {
        t := &Timer{
                r: runtimeTimer{when: when(d),
                        f:    goFunc,
                        arg:  f,
                },
        }
        startTimer(&t.r)
        return t
}
5 作为返回值
// $GOROOT/src/strings/strings.go
func makeCutsetFunc(cutset string) func(rune) bool {if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {return func(r rune) bool {return r == rune(cutset[0])
                }
        }
        if as, isASCII := makeASCIISet(cutset); isASCII {return func(r rune) bool {return r < utf8.RuneSelf && as.contains(byte(r))
                }
        }
        return func(r rune) bool {return IndexRune(cutset, r) >= 0 }
}

函数除了像下面那些例子,函数还能够被放入数组、切片、map 等构造体中,能够像其余类型变量一样被赋值给 interface{}、你也能够建设函数类型的 channel。

二 函数的非凡使用

1 显示转型
var x int = 5
var y int32 = 6
fmt.Println(x + y) 

运行这段代码,会报错,通知你类型不匹配,你须要进行类型转换,如下

var x int = 5
var y int32 = 6
fmt.Println(x + int(y))

函数是一等公民,下面是对变量进行的操作,同样也能够使用到函数中,即函数也能够被显示转型。

package main

import (
        "fmt"
        "net/http"
)

func hi(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w,“Hi, Gopher!")
}                    

func main() {http.ListenAndServe(":8080", http.HandlerFunc(hi))
}

下面就是 web server 的例子。当用户在浏览器中输出端口 8080 时,会显示 Hi, Gopher!

咱们看一下 ListenAndServe 的源码

// $GOROOT/src/net/http/server.go
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}
        return server.ListenAndServe()}

http 申请交给其第二个参数 handler 解决,而 handler 参数类型是 http.Handler 接口

// $GOROOT/src/net/http/server.go
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}

该接口只有一个办法 ServeHTTP,原型就是 func(http.ResponseWriter, *http.Request),与后面定义的 hi 原型统一。但无奈间接将 hi 作为参数传入,否则报错(函数 hi 并未实现接口 Handler 办法,无奈将其赋值给 Handler 类型参数)
下面的代码咱们是应用 http.HandlerFunc(hi) 这样的形式,看看 http.HandlerFunc 源码:

// $GOROOT/src/net/http/server.go

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)
}

HandlerFunc 其实就是一个基于函数定义的新类型,它的底层类型为 func(ResponseWriter, *Request)。该类型有一个办法 ServeHTTP, 继而实现了 Handler 接口。
http.HandlerFunc(greeting) 这句语法上没有报错,是因为 HandlerFunc 的底层类型正是 func(ResponseWriter,*Request), 与 hi 的原型是统一的,这和咱们对应用过的整型变量的转型是统一的。代码如下:
type MyInt int
var a int = 666
b:=MyInt(a)

2 函数式编程

  func multiply(x, y int) int {return x * y}

func DoWork(x int) func(int) int {return func(y int) int {return multiply(x, y)
    }
}

func main() {timesTwo := DoWork(6)
    fmt.Println(timesTwo(8))
}
执行下面的代码输入 48

将原承受 2 个参数的函数 multiply 转换为承受一个参数的 DoWork。通过 DoWork 函数结构 multiply。
这里的函数利用了函数的两点个性:
1 在函数体内定义函数,通过返回值返回给内部。
2 闭包

闭包是在函数外部定义的匿名函数,并且容许该匿名函数拜访它的内部函数的作用域。实际上,闭包是将函数外部和函数内部链接起来的桥梁。下面 DoWork 外部的匿名函数就是一个闭包。

函数式编程
type Do interface {Work(func(jobName string,content string))
}

type Job struct {
    JobName string
    Content string
}

func (j Job) Work(fn func(jobName string,content string))  {fn(j.JobName, j.Content)
}

func WorkDesc(jobName string,content string){fmt.Println(jobName+""+content)
}
func main() {job := Job{JobName: "开发工作:",Content: "研发"}
    job.Work(WorkDesc)
}

运行下面的代码,后果:
开发工作:研发

提早加载。

以斐波那契数列为例,它也是一种递归。代码如下:

func fab(n int) int {
        if n == 1 {return 1} else {return n * fab(n-1)
        }
}
 
func main() {fmt.Printf("%d\n", fab(10))
}

正文完
 0