如何在Go中定义一个接受任意数量参数的函数类型?

huangapple go评论125阅读模式
英文:

How to define a function type which accepts any number of arguments in Go?

问题

我试图编写一个函数,它接受任何其他函数并在其周围包装一个新函数。这是我目前尝试的代码:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func protect(unprotected func(...interface{})) func(...interface{}) {
  6. return func(args ...interface{}) {
  7. fmt.Println("protected")
  8. unprotected(args...)
  9. }
  10. }
  11. func main() {
  12. a := func() {
  13. fmt.Println("unprotected")
  14. }
  15. b := protect(a)
  16. b()
  17. }

当我编译时,我得到以下错误:

  1. cannot use a (type func()) as type func(...interface { }) in function argument

为什么没有参数的函数与具有可变数量参数的函数不兼容?我该如何使它们兼容?

更新:
受保护的函数应与原始函数兼容:

  1. func take_func_int_int(f func(x int) int) int {
  2. return f(1)
  3. }
  4. func main() {
  5. a := func(x int) int {
  6. return 2 * x
  7. }
  8. b := protect(a)
  9. take_func_int_int(a)
  10. take_func_int_int(b)
  11. }
英文:

I try to write a function which takes any other function and wraps a new function around it. This is what I have tried so far:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func protect (unprotected func (...interface{})) (func (...interface{})) {
  6. return func (args ...interface{}) {
  7. fmt.Println ("protected");
  8. unprotected (args...);
  9. };
  10. }
  11. func main () {
  12. a := func () {
  13. fmt.Println ("unprotected");
  14. };
  15. b := protect (a);
  16. b ();
  17. }

When I compile this I get the error:

  1. cannot use a (type func()) as type func(...interface { }) in function argument

Why is a function without arguments not compatible to a function with a variable number of arguments? What can I do to make them compatible?

Update:
The protected function should be compatible with the original:

  1. func take_func_int_int (f func (x int) (y int)) (int) {
  2. return f (1)
  3. }
  4. func main () {
  5. a := func (x int) (y int) {
  6. return 2 * x
  7. }
  8. b := protect (a)
  9. take_func_int_int (a)
  10. take_func_int_int (b)
  11. }

答案1

得分: 17

在Go语言中,类型是非常具体的。你可以尝试使用以下代码:

  1. a := func(_ ...interface{}) {
  2. fmt.Println("unprotected")
  3. }

func (...interface{})并不意味着“任何接受任意数量任何类型参数的函数”,而是意味着“只有接受可变数量interface{}类型参数的函数”。

或者,你可以使用interface{}reflect包,而不是func(...interface{})。可以参考http://github.com/hoisie/web.go中的示例。

编辑:具体来说,是这样的:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func protect(oldfunc interface{}) (func (...interface{})) {
  7. if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
  8. panic("protected item is not a function")
  9. }
  10. return func (args ...interface{}) {
  11. fmt.Println("Protected")
  12. vargs := make([]reflect.Value, len(args))
  13. for n, v := range args {
  14. vargs[n] = reflect.ValueOf(v)
  15. }
  16. reflect.ValueOf(oldfunc).Call(vargs)
  17. }
  18. }
  19. func main() {
  20. a := func() {
  21. fmt.Println("unprotected")
  22. }
  23. b := func(s string) {
  24. fmt.Println(s)
  25. }
  26. c := protect(a)
  27. d := protect(b)
  28. c()
  29. d("hello")
  30. }

输出结果为:

  1. Protected
  2. unprotected
  3. Protected
  4. hello

编辑:回答更新的问题

就像我之前说的,Go语言中的类型是非常具体的。protect函数返回的类型是func(...interface{}),它永远不会被赋值给func(int)int。我认为你可能要么过度设计了你的问题,要么对问题有误解。然而,下面是一个强烈不推荐的代码片段,可以使其工作。

首先,修改protect函数以返回值:

  1. func protect(oldfunc interface{}) (func (...interface{}) []interface{}) {
  2. if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
  3. panic("protected item is not a function")
  4. }
  5. return func (args ...interface{}) []interface{} {
  6. fmt.Println("Protected")
  7. vargs := make([]reflect.Value, len(args))
  8. for n, v := range args {
  9. vargs[n] = reflect.ValueOf(v)
  10. }
  11. ret_vals := reflect.ValueOf(oldfunc).Call(vargs)
  12. to_return := make([]interface{}, len(ret_vals))
  13. for n, v := range ret_vals {
  14. to_return[n] = v.Interface()
  15. }
  16. return to_return
  17. }
  18. }

然后创建一个转换函数:

  1. func convert(f func(...interface{}) (func(int) int) {
  2. return func(x int) int {
  3. r := f(x)
  4. return r[0].(int)
  5. }
  6. }

然后你的调用将如下所示:

  1. take_func_int_int(convert(b))

但我保证这不是你真正想做的。

退一步,尝试重新思考问题。我在这些示例中完全破坏了类型安全性。你想要实现什么?

英文:

Types are pretty concrete in Go. You could try

  1. a := func(_ ...interface{}) {
  2. fmt.Println("unprotected")
  3. }

func (...interface{}) does not mean "any function that takes any number of any kind of arguments", it means "only a function which takes a variable number of interface{} arguments"

Alternatively rather than func(...interface{}) you can just use interface{} and the reflect package. See http://github.com/hoisie/web.go for an example.

EDIT: Specifically, this:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func protect(oldfunc interface{}) (func (...interface{})) {
  7. if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
  8. panic("protected item is not a function")
  9. }
  10. return func (args ...interface{}) {
  11. fmt.Println("Protected")
  12. vargs := make([]reflect.Value, len(args))
  13. for n, v := range args {
  14. vargs[n] = reflect.ValueOf(v)
  15. }
  16. reflect.ValueOf(oldfunc).Call(vargs)
  17. }
  18. }
  19. func main() {
  20. a := func() {
  21. fmt.Println("unprotected")
  22. }
  23. b := func(s string) {
  24. fmt.Println(s)
  25. }
  26. c := protect(a)
  27. d := protect(b)
  28. c()
  29. d("hello")
  30. }

Ouput is

  1. Protected
  2. unprotected
  3. Protected
  4. hello

EDIT: To answer the update

Like I said above, types are pretty concrete in Go. The protect function returns a type func(...interface{}) which will never be assignable to func(int)int. I think you're probably either over-engineering your problem or misunderstanding it. However, here's a highly discouraged code snippet that would make it work.

First change protect to also return values:

  1. func protect(oldfunc interface{}) (func (...interface{}) []interface{}) {
  2. if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
  3. panic("protected item is not a function")
  4. }
  5. return func (args ...interface{}) []interface{} {
  6. fmt.Println("Protected")
  7. vargs := make([]reflect.Value, len(args))
  8. for n, v := range args {
  9. vargs[n] = reflect.ValueOf(v)
  10. }
  11. ret_vals := reflect.ValueOf(oldfunc).Call(vargs)
  12. to_return := make([]interface{}, len(ret_vals))
  13. for n, v := range ret_vals {
  14. to_return[n] = v.Interface()
  15. }
  16. return to_return
  17. }
  18. }

Then make a convert function:

  1. func convert(f func(...interface{}) (func(int) int) {
  2. return func(x int) int {
  3. r := f(x)
  4. return r[0].(int)
  5. }
  6. }

Then your call would look like

  1. take_func_int_int(convert(b))

##But I promise this isn't what you actually want to do.
Step back and try to rework the problem. I've completely killed type-safety in these examples. What are you trying to accomplish?

答案2

得分: 2

包 main

导入 "fmt"

// 这是一个接受任意数量的 int 参数的函数。
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}

func main() {

  1. // 可变参数函数可以像通常一样使用单独的参数进行调用。
  2. sum(1, 2)
  3. sum(1, 2, 3)
  4. // 如果您已经有一个包含多个参数的切片,
  5. // 可以使用 `func(slice...)` 将它们应用于可变参数函数,就像这样。
  6. nums := []int{1, 2, 3, 4}
  7. sum(nums...)

}

英文:
  1. package main
  2. import "fmt"
  3. // Here's a function that will take an arbitrary number
  4. // of `int`s as arguments.
  5. func sum(nums ...int) {
  6. fmt.Print(nums, " ")
  7. total := 0
  8. for _, num := range nums {
  9. total += num
  10. }
  11. fmt.Println(total)
  12. }
  13. func main() {
  14. // Variadic functions can be called in the usual way
  15. // with individual arguments.
  16. sum(1, 2)
  17. sum(1, 2, 3)
  18. // If you already have multiple args in a slice,
  19. // apply them to a variadic function using
  20. // `func(slice...)` like this.
  21. nums := []int{1, 2, 3, 4}
  22. sum(nums...)
  23. }

huangapple
  • 本文由 发表于 2011年5月18日 23:48:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/6047478.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定