英文:
Generic remove() function for slices
问题
我有3个切片(foos,bars,bazs),每个切片都填充有不同类型的结构体。为了减少一些样板代码,我想创建一个通用的remove(slice, struct) slice函数,类似于标准库中提供的**append()**的相反操作。
这些结构体都不会是指针,所以不需要将它们置为nil。我尝试使用**interface{}**来实现期望的结果,但没有成功。当前的实现使用了类型切换(Type Switch),然后有一个几乎完全复制粘贴的remove()函数(在下面的playground链接中有示例),用于从切片中删除元素。随着项目的扩展,这种样板代码会越来越多。
尝试的示例:
https://play.golang.org/p/9UPRIIp5M2
函数输入:[]slices,struct
期望输出:
如果找到了struct,则修改后的(删除了struct的)[]slices
如果没有找到struct,则保持不变的[]slices
如果这个实现简单而容易,我想它应该已经存在于标准库中。然而,向经验丰富的专业人士寻求建议,询问我所尝试的是否可行,从来没有坏处。
谢谢您的时间。
英文:
I have 3 slices (foos, bars, bazs) that are each populated with a different type of struct. In an attempt to remove some boilerplate code, I wanted to create a generic remove(slice, struct) slice function. Similar to being the opposite of append() provided in the standard.
None of the structs will be pointers so there's no need to nil them. I've flirted with the idea of using interface{} to get the desired result to no avail. Current implementation uses a Type Switch and then has a near copy-pasted remove() (example in playground link below) to delete from the slice. As I continue to expand the project- it will grow to more boilerplate.
Example of what is being attempted:
https://play.golang.org/p/9UPRIIp5M2
Function input: []slices, struct
Expected output:
Modified (removed struct) []slices if struct is found
Or, Unmodified []slices if it isn't.
If it was simple and easy to implement. I imagined it would already exist in the standard. However, it never hurts to get the advice from more seasoned professionals as to if what I am attempting to do is even possible.
Thank you for you time.
答案1
得分: 3
尝试使Go语言具有泛型是新手Go开发人员的一个巨大陷阱。停止吧。你正在节省五行代码:
for i := len(foos) - 1; i >= 0; i-- {
if foos[i] == foo1 {
foos = append(foos[:i], foos[i+1:]...)
}
}
是的,在泛型语言中,你可以将这五行代码封装成一个漂亮的标准库方法,但Go不是一种泛型语言。尝试使用反射来实现这一点会很慢,但这不是避免使用它的原因。反射非常复杂。很难弄对。你将花费比重写这五行代码12次(包括修复你意外剪切/粘贴错误的时间和你弄错i--
的一次)更多的时间来理解Value
并追踪奇怪的边界情况。只需直接编写它们。
直接编写代码可以让你决定什么是相等的。它让你决定是否在找到第一个匹配项后停止搜索,还是继续遍历整个列表。它让你根据程序的实际需求来做事,而不是关注某个泛型程序可能在将来需要的东西。
我喜欢泛型编程。很少有什么比在Haskell中创建一个优雅的fold更让我开心。但这不是Go的方式。在Go中,通常只需编写代码,保持简单和明显,然后继续前进。
Andy提到了一个很好的观点,如果你经常这样做,list
可能是一种更好的数据结构。我经常发现,当我有三种类型似乎都有相似方法时,它们实际上应该是一个单一的结构的一部分(你真的需要在这里分开的列表吗?)但无论如何,除非你有一个非常专门的问题,你真的需要“任何东西”而不是“这些少量的东西”之一,否则要远离反射。
(你提到了append()
很值得注意。我认为在Go中不可能编写append()
。这就是为什么它必须成为语言的一部分而不是标准库函数的原因。当我开始使用Go时,我认为这是语言的一个重大缺陷。我在Go中工作的时间越长,我发现它并不那么重要。你只需编写代码并继续前进。)
英文:
Trying to make Go generic is one of the great pitfalls for new Go devs. Stop. You're saving five of lines of code:
for i := len(foos) - 1; i >= 0; i-- {
if foos[i] == foo1 {
foos = append(foos[:i], foos[i+1:]...)
}
}
Yes, in generic languages, you would wrap those five lines up in to a nice stdlib method, but Go is not a generic language. Trying to do this with reflection is slow, but that's not the reason to avoid it. Reflection is very complicated. It's hard to get it right. You'll spend much more time figuring out Value
and chasing weird corner cases than you'll spend rewriting those five lines of code 12 times (including fixing the time you accidentally cut/paste it wrong, and the one time you mess up i--
). Just write them.
Just writing the code lets you decide what equality means. It lets you decide whether to stop searching at the first match, or keep going through the whole list. It lets you do what this program needs rather than focusing on what some generic program might someday need.
I love generic programming. Few things make me happier than creating an elegant fold in Haskell. But that's not the way of Go. In Go you generally just write the code, keep it simple and obvious, and move on.
Andy makes a good point that if you have to do this a lot, list
may be a better data structure. And I often find that when I have three types that all seem to have parallel methods, it turns out that they should all have been part of a single struct (do you really need separate lists here at all?) But in any case, stay away from reflection unless you have a very specialized problem where you really mean "anything" rather than "one of these short list of things."
(It's noteworthy that you call out append()
. I don't think it's possible to write append()
in Go. That's why it had to be part of the language rather than a stdlib function. When I started working in Go, I took that as a significant flaw in the language. The longer I've worked in Go, the more I've found it not to matter so much. You just write the code and move on.)
答案2
得分: 1
我看了你的示例代码和其中的错误。要了解更多关于interface slice
的内容,可以参考这个Go wiki和SliceTricks。
通用实现有点复杂,你可以选择基于类型的实现。然而,基于类型的实现会带来更多的样板代码/重复代码。
你需要使用reflect包来实现你的目的,正如@stephen-weinberg在评论中提到的。
所以最好的起点是尝试使用这个库github.com/anzhihun/generic,并阅读库的代码,然后自己实现。这个库使用了反射。
英文:
I had a look on your sample code and its errors. To learn more about interface slice
is to refer this Go wiki and SliceTricks.
Generic implementation is bit complex instead you can go with type based implementation. However Type based implementation brings more code on boilerplate/repetitive.
You need to use reflect package for your purpose as @stephen-weinberg mentioned in the comment.
So best starting point would be; try this library github.com/anzhihun/generic and go through library codebase, implement on your own. This library uses reflection.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论