本文视频地址
函数是日常工作中非常罕用的,无论是C语言,C++、Java、Python、JavaScript 都是必不可少的。
Go语言函数的特点如下:
1 func关键字结尾。
2 反对多返回值。
3 反对具名返回值。
4 反对递归调用。
5 反对同类型的可变参数。
6 反对defer,让函数优雅的返回。
一 GO语言中如何让函数变成 一等公民 的?
1 失常创立。规范库代码如下
// $GOROOT/src/fmt/print.gofunc 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.gofunc 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.gotype HandlerFunc func(ResponseWriter, *Request)
4 作为入参
$GOROOT/src/time/sleep.gofunc 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.gofunc 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 = 5var y int32 = 6fmt.Println(x + y)
运行这段代码,会报错,通知你类型不匹配,你须要进行类型转换,如下
var x int = 5var y int32 = 6fmt.Println(x + int(y))
函数是一等公民,下面是对变量进行的操作,同样也能够使用到函数中,即函数也能够被显示转型。
package mainimport ( "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.gofunc 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.gotype 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.gotype 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))}