56.笔记go语言——go的函数类型
对于动态脚本语言Ruby,JavaScript,Python都有higher-order函数。但是很难直接转换成G知识,虽然GO看上去是同样方式获取。另一方面,之前从事静态语言特别是面向对象语言例如C++,JAVA的,那么问题是使用higher-order函数很少。
很难直接从之前编程中直接跨越过来,所以蛤蟆也是需要学习,那么和蛤蟆一起开始吧。
匿名函数闭包
Go语言支持匿名函数和闭包。
在GO中定义一个匿名函数非常简单
func() {
fmt.Println("hello")
}
不过这个很少用,通常是保存到一个变量中,订到数据结构中,或传递到其他函数。
通常常见的如下代码:
fn :=func() {
fmt.Println("hello")
}
这样fn变量就是一个函数了,类型是func().当调用fn()的时候就会激活函数,或赋值给其他感兴趣的函数。当然,因为有闭包,可以引用在函数范围中的一些数据。如下:
x := 5
fn :=func() {
fmt.Println("x is", x)
}
fn()
x++
fn()
输出
X is 5
X is 6
函数收集
在使用数据类型的地方都可以使用函数,例如,可以创建slice函数组,随机选择函数并执行。定义一个binFunc二进制函数,使用2个整型,返回一个整型。那么可以使用binFunc来替换func(int,int)int了。
代码如下:
packagemain
import"math/rand"
import"time"
import"fmt"
typebinFuncfunc(int,int)int
funcmain(){
//seedyourrandomnumbergenerator.
rand.Seed(time.Now().Unix())
//createasliceoffunctions
fns:=[]binFunc{
func(x,yint)int{returnx+y},
func(x,yint)int{returnx-y},
func(x,yint)int{returnx*y},
func(x,yint)int{returnx/y},
func(x,yint)int{returnx%y},
}
//pickoneofthosefunctionsatrandom
fn:=fns[rand.Intn(len(fns))]
//andexecuteit
x,y:=12,5
fmt.Println(fn(x,y))
}
执行结果:
17
当然是随机了。
函数作为域
通常,使用函数类型作为一个结构中的域。这样可以让我们给函数附加一些信息,例如标签等。
如下代码:
packagemain
import"math/rand"
import"time"
import"fmt"
typeopstruct{
namestring
fn func(int,int)int
}
funcmain(){
//seedyourrandomnumbergenerator
rand.Seed(time.Now().Unix())
//createasliceofops
ops:=[]op{
{"add",func(x,yint)int{returnx+y}},
{"sub",func(x,yint)int{returnx-y}},
{"mul",func(x,yint)int{returnx*y}},
{"div",func(x,yint)int{returnx/y}},
{"mod",func(x,yint)int{returnx%y}},
}
//pickoneofthoseopsatrandom
o:=ops[rand.Intn(len(ops))]
x,y:=12,5
fmt.Println(o.name,x,y)
fmt.Println(o.fn(x,y))
}
执行如下:
add 12 5
17
此外,函数还可以被存储在maps中。
递归函数类型
另一个有意思的函数类型是允许定义递归函数类型。将自己类型作为参数或者使用自己的类型作为返回值。
例如递归函数:
typewalkFn func(*int) walkFn
合法的walkFn可以如下walkEqual函数
func walkEqual(i*int) walkFn {
*i += rand.Intn(7) - 3
return walkEqual
}
函数返回一个随机值并-3,然后返回本身。
函数作为接口值
GO语言中的函数类型也可以有方法。很少见。
任何有方法的类型都可以满足接口,所以函数类型可以来合法化接口类型。
GO中方法可以有指针或值接收。
PS:任何有Error() string方法的都是合法的error类型,函数可以作为或和error.
代码如下:
packagemain
import"fmt"
typebinFuncfunc(int,int)int
funcadd(x,yint)int{
returnx+y
}
func(fbinFunc)Error()string{
return"binFuncerror"
}
funcmain(){
varerrerror
err=binFunc(add)
fmt.Println(err)
}
在这个例子中,将add函数从func(int,int) int转换为binFunc。
然后实现func(int,int) int的error.
再定义如下:
type loudBinFunc func(int, int) int
func (f loudBinFunc) Error() string {
return "THIS ERROR IS A LOT LOUDER"
}
定义类型loudBinFunc,然后实现Error()函数。如果同时有 binFunc
和 loudBinFunc
,那么go就不知道如何转换add函数了。一开始可能会反感,但是其实会让我们避免代码BUG。
在标准库net/http包中,可以看到使用函数类型来满足接口的例子。
作为Channel的函数
使用Channels来合并函数类型。
定义channel类型如下:
chan func()
创建函数组成的slice如下:
x := 10
fns := []func(){
func() { x += 1 },
func() { x -= 1 },
func() { x *= 2 },
func() { x /= 2 },
func() { x *= x },
}
在定义一个函数来随机获取,如下:
func pickFunc(fns ...func()) func() {
return fns[rand.Intn(len(fns))]
}
然后创建函数的channel.
c := make(chan func())
在定义一个函数,将CHANNEL,数量描述,函数集作为参数,如下:
func produce(c chan func(), n int, fns ...func()) {
defer close(c)
for i := 0; i < n; i++ {
c <- pickFunc(fns...)
}
}
在函数开始处,使用了deferclose( c)来确保channel能得到关闭。
何在一起代码如下:
package main
import (
"fmt"
"math/rand"
"time"
)
var delay = 200 * time.Millisecond
func pickFunc(fns ...func()) func() {
return fns[rand.Intn(len(fns))]
}
func produce(c chan func(), n int, fns ...func()) {
defer close(c)
for i := 0; i < n; i++ {
c <- pickFunc(fns...)
}
}
func main() {
// time is frozen on Playground, so this is always the same.
rand.Seed(time.Now().Unix())
x := 10
fns := []func(){
func() { x += 1 },
func() { x -= 1 },
func() { x *= 2 },
func() { x /= 2 },
func() { x *= x },
}
c := make(chan func())
go produce(c, 10, fns...)
for fn := range c {
fn()
fmt.Println(x)
time.Sleep(delay)
}
}
执行输出如下:
20
10
20
40
1600
3200
3201
6402
40985604
81971208
友情链接
http://jordanorelli.com/post/42369331748/function-types-in-go-golang