Golang仿函数实现方法及效率测试

12 篇文章 1 订阅
3 篇文章 0 订阅

在C++ STL中,仿函数(functors)被大量用作改变算法的内在行为。
由于Golang不支持泛型,所以没法像C++那样灵活的使用仿函数。但是Golang有interface,函数是”一等公民”(可赋值给指定类型变量),因此,在Golang中实际上也可以像仿函数那样,通过具有相同参数和返回值的方法声明的不同对象,实现行为的差异化。
下面,详细讲述,Golang中的实现方法:
以下通过不同方法实现 Lesser(int,int)bool 和 Greater(int,int)bool的不同行为举例。
1. 通过interface实现

type Comparer interface {
    F(left, right int) bool
}

//create cmp object by name
func CreateComparer(cmpName string) (r Comparer) {
    switch cmpName {
    case "": //default Lesser
        fallthrough
    case "Lesser":
        r = Lesser{}
    case "Greater":
        r = Greater{}
    default: //unsupport name
        panic(cmpName)
    }
    return
}

//Lesser
type Lesser struct{}

func (this Lesser) F(left, right int) (ok bool) {
    ok = left < right
    return
}

//Greater
type Greater struct{}

func (this Greater) F(left, right int) (ok bool) {
    ok = right < left
    return
}

2.通过函数对象实现

type CmpFunc func(left, right int) bool

//create cmp object by name
func GetCmpFunc(cmpName string) (r CmpFunc) {
    switch cmpName {
    case "": //default Lesser
        fallthrough
    case "Lesser":
        r = Less
    case "Greater":
        r = Great
    default: //unsupport name
        panic(cmpName)
    }
    return
}

//Lesser
func Less(left, right int) (ok bool) {
    ok = left < right
    return
}

//Greater
func Great(left, right int) (ok bool) {
    ok = right < left
    return
}

3.通过转调对象实现

type CmpObj byte

const (
    CMP_LESS CmpObj = iota
    CMP_GREAT
)

func (me CmpObj) F(left, right int) (ok bool) {
    switch me {
    case CMP_LESS:
        ok = Less(left, right)
    case CMP_GREAT:
        ok = Great(left, right)
    default:
        panic(me)
    }
    return
}

以下是3种方法的效率测试:

var (
    cmp1 = CreateComparer("Lesser")
    cmp2 = GetCmpFunc("Lesser")
    cmp3 = CMP_LESS
    N    = 100000000
)

func TestSize(t *testing.T) {
    fmt.Println("Interface", unsafe.Sizeof(cmp1))
    fmt.Println("Func", unsafe.Sizeof(cmp2))
    fmt.Println("Obj", unsafe.Sizeof(cmp3))
}

func Benchmark_Interface(b *testing.B) {
    for i := 0; i < N; i++ {
        cmp1.F(1, 2)
    }
}
func Benchmark_Func(b *testing.B) {
    for i := 0; i < N; i++ {
        cmp2(1, 2)
    }
}
func Benchmark_Obj(b *testing.B) {
    for i := 0; i < N; i++ {
        cmp3.F(1, 2)
    }
}

//Interface 16
//Func 8
//Obj 1
//Benchmark_Interface-4     1000000000           0.55 ns/op
//Benchmark_Func-4          2000000000           0.19 ns/op
//Benchmark_Obj-4           2000000000           0.24 ns/op

结论如下:
用interface实现多态,会占用两个指针(16字节空间) 执行效率上 大概慢一倍
使用函数指针 占用1个指针(8字节) 执行效率最高 但使用起来不够灵活
使用转调对象 只需要1个字节 执行效率跟函数指针差不多

推荐使用第三种方法(转调对象)的方法,使用数值枚举标识多路分发逻辑,效率上几乎没有损失,对外部引用对象的空间需求也比较小。

我将以上测试代码放在这里,欢迎查阅:
https://github.com/vipally/glab/blob/master/lab5/lab5_test.go

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值