我完全理解Go不提供对泛型的支持,而是选择用户在需要时创建自己的特定于类型的方法。
但是,我想知道是否有更有效的方法在数据结构上创建特定的map
函数,而不涉及遍历整个列表并应用该函数,或者这是其他语言< em> with generics support 在幕后做。
示例:
func map(list []string, op func(string)string) []string {
ouput := make([]string, len(list))
for i, v := range list {
output[i] = op(v)
}
return output
}
谢谢!
答案 0 :(得分:4)
仅供参考,map
是保留字,因此您不能完全按照小写map
编写函数。
这可能就像你能得到的一样好。泛型不会让你分配新内存。
使用append()稍微快一些,而不是在开始时将整个切片初始化为空字符串。例如:
func Map(list []string, op func(string) string) []string {
output := make([]string, 0, len(list))
for _, v := range list {
output = append(output, op(v))
}
return output
}
这让我的速度提高了10%。
更新:这对于很短的切片来说才是真的。这是一个更彻底的基准 - 在更长的切片上使用追加实际上更慢。我也试过并行化,这只是在更大的切片上的开销。
代码: https://gist.github.com/8250514
输出(测试名称末尾的数字是切片长度):
go test -bench=".*" -test.cpu=2
BenchmarkSliceMake10-2 5000000 473 ns/op
BenchmarkSliceMake100-2 500000 3637 ns/op
BenchmarkSliceMake1000-2 50000 43920 ns/op
BenchmarkSliceMake10000-2 5000 539743 ns/op
BenchmarkSliceAppend10-2 5000000 464 ns/op
BenchmarkSliceAppend100-2 500000 4303 ns/op
BenchmarkSliceAppend1000-2 50000 51172 ns/op
BenchmarkSliceAppend10000-2 5000 595650 ns/op
BenchmarkSlicePar10-2 500000 3784 ns/op
BenchmarkSlicePar100-2 200000 7940 ns/op
BenchmarkSlicePar1000-2 50000 50118 ns/op
BenchmarkSlicePar10000-2 5000 465540 ns/op
答案 1 :(得分:3)
是的,通常这正是泛型用于的东西。如果你愿意,这些函数仍然可以使用反射在Go中编写,尽管它们很多更慢。例如,请参阅我的github.com/synful/illegal/generics包,它使用反射来实现经典的泛型函数。我实际上并没有对这些进行基准测试,但如果它们与您提供的特定类型实现的性能接近,我会感到非常惊讶。
编辑:只是为了踢,我复制了Wes Freeman的第一个测试,并在我的基于反射的Map上运行它。这是在一个相当不足的服务器上运行,但仍然。结果不言自明(根据Wes的结果衡量缓慢)。
BenchmarkSliceMake10-2 200000 14091 ns/op [30.0x slower]
BenchmarkSliceMake100-2 10000 112137 ns/op [30.83x slower]
BenchmarkSliceMake1000-2 2000 1177498 ns/op [26.810x slower]
BenchmarkSliceMake10000-2 100 11513085 ns/op [21.3307x slower]
注意:我特别使用了这个测试,因为我的实现预先分配了切片。