Go中唯一函数的集合 [英] Collection of Unique Functions in Go

查看:133
本文介绍了Go中唯一函数的集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在go中实现一组函数。上下文是一个事件服务器;我想阻止(或者至少警告)为一个事件多次添加相同的处理程序。



我已经读过地图是习惯性使用的,因为轻松检查会员资格:

  if _,ok:= set [item]; OK {
//不添加商品
}其他商品{
//添加商品
}

虽然我在函数中使用这个范例时遇到了一些麻烦。这是我第一次尝试:

  //这不是实际的签名
类型EventResponse func(args interface {} )

类型EventResponseSet map [* EventResponse] struct {}

func(ers EventResponseSet)Add(r EventResponse){
if _,ok:= ers [ &安培; R]; OK {
//在此警告
返回
}
ers [& r] =结构{} {}
}

func (ers EventResponseSet)删除(r EventResponse){
//如果key不存在,无所谓
delete(ers,& r)
}

很明显为什么这不起作用:Go中的函数不是引用类型,尽管有些人会告诉你它们是。 我有证据,但我们不需要它,因为语言规范说除了地图,切片和指针是按值传递的。



尝试2:

  func(ers EventResponseSet)Add(r * EventResponse){
// ...
}

这有几个问题:


  • 任何EventResponse都必须声明为 fn:= func(args interface {}){} ,因为您无法使用通常的方式声明函数。

  • 你不能传递一个闭包。 使用包装不是一个选项,因为任何传递给包装的函数都会得到一个新地址从包装 - 没有功能将被地址唯一标识,并且所有这些仔细的计划是徒劳的。




是我很愚蠢的是不接受定义函数作为变量作为一个解决方案?是否有另一个(好的)解决方案?



要清楚的是,我接受有些情况我无法捕捉(关闭),这很好。我设想的用例是定义了一些处理程序,并且相对安全,我不会无意中将两个相同的事件添加到相同的事件两次,如果有意义的话。

可以使用Uvelichitel提供的 reflect.Value ,或者函数地址作为 string $ c>由 fmt.Sprint() 或通过 uintptr 地址noreferrer> reflect.Value.Pointer() (更多答案

- 比较2-functions-in-go / 34901677#34901677>如何比较Go中的2个功能?
),但我建议不要。

由于语言规范不允许比较函数值,所以它允许接收他们的地址,您不能保证在您的时间程序将始终工作,包括特定的运行,并包括不同的(将来的)Go编译器。我不会使用它。



由于规范对此非常严格,这意味着编译器可以生成代码,例如可以在运行时更改函数的地址例如卸载一个未使用的函数,然后再次加载它,如果需要再次)。我不知道目前这种行为,但这并不意味着未来的Go编译器不会利用这样的事情。



如果你存储一个函数地址(以任何格式),该值不再被视为保持函数值。如果没有其他人能够拥有函数值,那么生成的代码(和Go运行时)将自由修改/重定位函数(从而更改其地址) - 而不违反规范,Go的类型安全性。所以你不能正确地生气和责怪编译器,但只有你自己。



如果你想检查重用,你可以使用接口值。



假设您需要带签名的函数:

  func(p ParamType)RetType 

创建界面:

  type EventResponse接口{
Do(p ParamType)RetType
}

例如,您可以有一个未导出的 struct 类型,并且一个指向它的指针可以实现您的 EventResponse 界面。使一个导出的函数返回单个值,所以不会创建新的值。



例如:

类型myEvtResp struct {} 

func(m * myEvtResp)Do(p ParamType)RetType {
//您的逻辑在这里
}

var single =& myEvtResp {}

func Get()EventResponse {return single}

是否真的需要将实现隐藏在一个包中,并且只创建并发布一个实例?不幸的是,因为否则你可以创建其他值,比如& myEvtResp {} ,它们可能是不同的指针,它们仍然具有相同的 Do()方法,但接口封装值可能不相等:


接口值是可比的。如果两个界面值具有相同动态类型和相等的动态值,或者两者都具有相同的值 nil



[...和...]



指针值具有可比性。如果两个指针值指向相同的变量或者两者的值均为零,则两个指针值相等。指向独特的零大小的变量可能相等,也可能不相等。


类型 * myEvtResp 实现 EventResponse 和所以你可以注册它的值(唯一的值,可以通过 Get())访问。您可以使用 map [EventResponse] bool 类型的映射,您可以在其中存储注册的处理程序,将接口值存储为键, true 作为值。使用不在映射中的键索引映射会生成映射值类型的零值。所以如果map的值类型是 bool ,用一个不存在的键索引它会导致 false - 告诉它不在地图中。使用已经注册的 EventResponse (一个现有的键)进行索引将导致存储的值 - true - 告诉它在地图,它已经注册。



您可以简单地检查一个已经注册的人:

  type EventResponseSet map [* EventResponse] bool 

func(ers EventResponseSet)Add(r EventResponse){
if ers [r] {
// //在此警告
return
}
ers [r] = true
}

关闭:为避免重复使用,这看起来有点太麻烦了。我同意,我不会为此而努力。但如果你想... ...

I am trying to implement a set of functions in go. The context is an event server; I would like to prevent (or at least warn) adding the same handler more than once for an event.

I have read that maps are idiomatic to use as sets because of the ease of checking for membership:

if _, ok := set[item]; ok {
    // don't add item
} else {
    // do add item
}

I'm having some trouble with using this paradigm for functions though. Here is my first attempt:

// this is not the actual signature
type EventResponse func(args interface{})

type EventResponseSet map[*EventResponse]struct{}

func (ers EventResponseSet) Add(r EventResponse) {
    if _, ok := ers[&r]; ok {
        // warn here
        return
    }
    ers[&r] = struct{}{}
}

func (ers EventResponseSet) Remove(r EventResponse) {
    // if key is not there, doesn't matter
    delete(ers, &r)
}

It is clear why this doesn't work: functions are not reference types in Go, though some people will tell you they are. I have proof, though we shouldn't need it since the language specification says that everything other than maps, slices, and pointers are passed by value.

Attempt 2:

func (ers EventResponseSet) Add(r *EventResponse) {
// ...
}

This has a couple of problems:

  • Any EventResponse has to be declared like fn := func(args interface{}){} because you can't address functions declared in the usual manner.

  • You can't pass a closure at all.

  • Using a wrapper is not an option because any function passed to the wrapper will get a new address from the wrapper - no function will be uniquely identifiable by address, and all this careful planning is for nought.

Is it silly of me to not accept defining functions as variables as a solution? Is there another (good) solution?

To be clear, I accept that there are cases that I can't catch (closures), and that's fine. The use case that I envision is defining a bunch of handlers and being relatively safe that I won't accidentally add one to the same event twice, if that makes sense.

解决方案

You could use reflect.Value presented by Uvelichitel, or the function address as a string acquired by fmt.Sprint() or the address as uintptr acquired by reflect.Value.Pointer() (more in the answer How to compare 2 functions in Go?), but I recommend against it.

Since the language spec does not allow to compare function values, nor does it allow to take their addresses, you have no guarantee that something that works at a time in your program will work always, including a specific run, and including different (future) Go compilers. I would not use it.

Since the spec is strict about this, this means compilers are allowed to generate code that would for example change the address of a function at runtime (e.g. unload an unused function, then load it again later if needed again). I don't know about such behavior currently, but this doesn't mean that a future Go compiler will not take advantage of such thing.

If you store a function address (in whatever format), that value does not count as keeping the function value anymore. And if no one else would "own" the function value anymore, the generated code (and the Go runtime) would be "free" to modify / relocate the function (and thus changing its address) – without violating the spec and Go's type safety. So you could not be rightfully angry at and blame the compiler, but only yourself.

If you want to check against reusing, you could work with interface values.

Let's say you need functions with signature:

func(p ParamType) RetType

Create an interface:

type EventResponse interface {
    Do(p ParamType) RetType
}

For example, you could have an unexported struct type, and a pointer to it could implement your EventResponse interface. Make an exported function to return the single value, so no new values may be created.

E.g.:

type myEvtResp struct{}

func (m *myEvtResp) Do(p ParamType) RetType {
    // Your logic comes here
}

var single = &myEvtResp{}

func Get() EventResponse { return single }

Is it really needed to hide the implementation in a package, and only create and "publish" a single instance? Unfortunately yes, because else you could create other value like &myEvtResp{} which may be different pointers still having the same Do() method, but the interface wrapper values might not be equal:

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

[...and...]

Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.

The type *myEvtResp implements EventResponse and so you can register a value of it (the only value, accessible via Get()). You can have a map of type map[EventResponse]bool in which you may store your registered handlers, the interface values as keys, and true as values. Indexing a map with a key that is not in the map yields the zero value of the value type of the map. So if the value type of the map is bool, indexing it with a non-existing key will result in false – telling it's not in the map. Indexing with an already registered EventResponse (an existing key) will result in the stored value – true – telling it's in the map, it's already registered.

You can simply check if one already been registered:

type EventResponseSet map[*EventResponse]bool

func (ers EventResponseSet) Add(r EventResponse) {
    if ers[r] {
        // warn here
        return
    }
    ers[r] = true
}

Closing: This may seem a little too much hassle just to avoid duplicated use. I agree, and I wouldn't go for it. But if you want to...

这篇关于Go中唯一函数的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆