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

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

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

问题

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

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()
}

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

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

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

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

func take_func_int_int(f func(x int) int) int {
	return f(1)
}

func main() {

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

	take_func_int_int(a)
	take_func_int_int(b)
}
英文:

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:

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 ();
}

When I compile this I get the error:

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:

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)
}

答案1

得分: 17

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

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

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

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

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

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")
}

输出结果为:

Protected
unprotected
Protected
hello

编辑:回答更新的问题

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

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

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
    }
}

然后创建一个转换函数:

func convert(f func(...interface{}) (func(int) int) {
    return func(x int) int {
        r := f(x)
        return r[0].(int)
    }
}

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

take_func_int_int(convert(b))

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

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

英文:

Types are pretty concrete in Go. You could try

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

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:

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")
}

Ouput is

Protected
unprotected
Protected
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:

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
	}
}

Then make a convert function:

func convert(f func(...interface{}) (func(int) int) {
    return func(x int) int {
        r := f(x)
        return r[0].(int)
    }
}

Then your call would look like

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() {

// 可变参数函数可以像通常一样使用单独的参数进行调用。
sum(1, 2)
sum(1, 2, 3)

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

}

英文:
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...)
}

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:

确定