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


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

我尝试编写一个函数,它接受任何其他函数,并围绕它包装一个新函数。这就是我迄今为止所尝试的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
   "fmt"
)

func protect (unprotected func (...interface{})) (func (...interface{})) {
    return func (args ...interface{}) {
        fmt.Println ("protected");
        unprotected (args...);
    };
}

func main () {
    a := func () {
        fmt.Println ("unprotected");
    };
    b := protect (a);
    b ();
}

当我编译这个时,我得到了错误:

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

为什么没有参数的函数与参数数目可变的函数不兼容?我该怎么做才能使它们兼容?

更新:保护功能应与原始功能兼容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func take_func_int_int (f func (x int) (y int)) (int) {
    return f (1)
}

func main () {

    a := func (x int) (y int) {
        return 2 * x
    }
    b := protect (a)

    take_func_int_int (a)
    take_func_int_int (b)
}


类型在Go中相当具体。你可以试试

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

func (...interface{})不表示"任何接受任何数量任何类型参数的函数",而是表示"仅接受可变数量接口参数的函数"

或者,您可以使用interface{}reflect包,而不是func(...interface{})。例如,请参阅http://github.com/hoisie/web.go。

编辑:特别是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
   "fmt"
   "reflect"
)

func protect(oldfunc interface{}) (func (...interface{})) {
    if reflect.TypeOf(oldfunc).Kind() != reflect.Func {
        panic("protected item is not a function")
    }
    return func (args ...interface{}) {
        fmt.Println("Protected")
        vargs := make([]reflect.Value, len(args))
        for n, v := range args {
            vargs[n] = reflect.ValueOf(v)
        }
        reflect.ValueOf(oldfunc).Call(vargs)
    }
}

func main() {
    a := func() {
        fmt.Println("unprotected")
    }
    b := func(s string) {
        fmt.Println(s)
    }
    c := protect(a)
    d := protect(b)
    c()
    d("hello")
}

输出为

1
2
3
4
Protected
unprotected
Protected
hello

编辑:回答更新

就像我上面说的,类型在go中是相当具体的。protect函数返回一个类型func(...interface{}),该类型永远不能分配给func(int)int。我认为你可能是过度设计了你的问题或者误解了它。但是,这里有一个非常不鼓励使用的代码片段。

第一个更改保护也返回值:

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

然后生成一个转换函数:

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

那么你的电话看起来像

1
take_func_int_int(convert(b))

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

退一步,尝试重新处理问题。在这些例子中,我完全消灭了类型安全。你想完成什么?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import"fmt"

// Here's a function that will take an arbitrary number
// of `int`s as arguments.
func sum(nums ...int) {
    fmt.Print(nums,"")
    total := 0
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}

func main() {

    // Variadic functions can be called in the usual way
    // with individual arguments.
    sum(1, 2)
    sum(1, 2, 3)

    // If you already have multiple args in a slice,
    // apply them to a variadic function using
    // `func(slice...)` like this.
    nums := []int{1, 2, 3, 4}
    sum(nums...)
}