项目地址: https://github.com/ahuigo/gofnext#decorator-cases
类似于 python 的 @functools.cache
cache decorator 菲波那切数列示例:
package main
import "fmt"
import "github.com/ahuigo/gofnext"
func main() {
var fib func(int) int
var fibCached func(int) int
fib = func(x int) int {
fmt.Printf("call arg:%d\n", x)
if x <= 1 {
return x
} else {
return fibCached(x-1) + fibCached(x-2)
}
}
fibCached = gofnext.CacheFn1(fib)
fmt.Println(fibCached(5))
fmt.Println(fibCached(6))
}
简写
var fib func(int) int
fib = func(x int) int {
fmt.Printf("call arg:%d\n", x)
if x <= 1 {
return x
} else {
return fib(x-1) + fib(x-2)
}
}
fib = gofnext.CacheFn1(fib, nil)
fmt.Println(fib(5))
1
keakon 2023-12-11 18:15:28 +08:00
看上去不够优雅,但是如果要适配任意数目的参数和返回值,只能用反射来做。
|
2
a132811 OP @keakon 多参数的话不需要用反射。
其实以前我用反射实现过,使用起来更复杂、且会损失类型检查,目前用泛型实现就简单许多了。 如果是多参数的话,需要用包装函数(不需要反射)将参数将降维(示例: https://github.com/ahuigo/gofnext#cache-function-with-more-params2 ) ps: 之所以没有封装成支持任意不定参或 any 类型的函数,是因为目前 go 的泛型参数不支持对约束再做断言。 如果你有更优雅方式的话,望回复告之,也欢迎 issue 、pr 。 |
3
keakon 2023-12-12 10:28:57 +08:00
@a132811 反射不会损失类型检查,可以动态获取参数类型并构造一个 struct ,但是做不到静态检查。实现自然是会复杂很多,但是对调用者而言是更简单的。不过既然你是做缓存,肯定是在意性能的,我就不推荐用反射了。
|
4
a132811 OP |
5
keakon 2023-12-12 21:30:18 +08:00
@a132811 我的意思是反射可以获取到原函数的每个参数的类型,你可以保存下来,调用时检查参数是不是对应的类型。
但是 Go 不是很动态的语言,反射和泛型也没法结合使用,导致泛型实现的接口没法返回正确的类型(只能是 interface{}),因此没法实现 demo 的 fibCached(x-1) + fibCached(x-2)。不过 demo 里对 fib() 的实现也是有侵入的。 比较类似的例子你可以参考这篇,最后为了优化实现得有点复杂,看看原理就好: https://keakon.uk/2023/03/24/%E6%8A%8A%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97delayed%E7%A7%BB%E6%A4%8D%E5%88%B0Go%E4%BA%86 |