【问题标题】:Generic remove() function for slices切片的通用 remove() 函数
【发布时间】:2017-12-18 16:26:16
【问题描述】:

我有 3 个切片(foos、bars、bazs),每个切片都填充了不同类型的结构。为了删除一些样板代码,我想创建一个通用的 remove(slice, struct) slice 函数。类似于标准中提供的 append() 的对立面。

没有一个结构是指针,所以没有必要将它们归零。我曾想过使用 interface{} 来获得所需的结果,但无济于事。当前的实现使用一个类型开关,然后有一个接近复制粘贴的 remove()(下面的游乐场链接中的示例)从切片中删除。随着我继续扩展该项目,它将发展为更多样板。

正在尝试的示例: 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.

如果它简单易行。我想象它已经存在于标准中。但是,从经验丰富的专业人士那里获得关于我正在尝试做的事情是否可行的建议永远不会有什么坏处。

感谢您的宝贵时间。

【问题讨论】:

  • 您需要为此使用反射。它不会是类型安全的并且会更慢。
  • 可能吗?是的。好主意?几乎可以肯定不会,原因已经提到:它会很慢。
  • 看起来我会熟悉反射,这样我就可以运行一些效率基准。运行基准测试后,我可能会选择退出并返回样板版本。感谢您的反馈。
  • 您需要对这些切片进行索引还是只是追加、删除、迭代?如果您不需要随机访问,您可能需要考虑不同的数据结构,例如链表(例如,golang.org/pkg/container/list)。

标签: generics go slice


【解决方案1】:

试图使 Go 通用化是新 Go 开发人员的一大陷阱。停止。你正在保存five of lines of code

for i := len(foos) - 1; i >= 0; i-- {
    if foos[i] == foo1 {
        foos = append(foos[:i], foos[i+1:]...)
    }
}

是的,在通用语言中,您可以将这 5 行代码包装成一个不错的 stdlib 方法,但 Go 不是通用语言。尝试通过反射来做到这一点很慢,但这不是避免它的原因。反射非常复杂。很难做到正确。您将花费更多的时间来找出Value 和追逐奇怪的极端案例,而不是花费 12 次重写这五行代码(包括修复您不小心剪切/粘贴错误的时间,以及一次您搞砸的时间) i--)。写出来就好了。

只需编写代码,您就可以决定相等的含义。它可以让您决定是在第一次匹配时停止搜索,还是继续搜索整个列表。它可以让你做这个程序需要做的事情,而不是专注于某个通用程序有一天可能需要做的事情。

我喜欢泛型编程。没有什么比在 Haskell 中创建一个优雅的折叠更让我高兴的了。但这不是 Go 的方式。在 Go 中,您通常只需编写代码,保持简单明了,然后继续。

Andy 提出了一个很好的观点,如果你必须经常这样做,list 可能是一个更好的数据结构。而且我经常发现,当我拥有三种似乎都具有并行方法的类型时,事实证明它们都应该是单个结构的一部分(您真的需要单独的列表吗?)但无论如何,请留下远离反思,除非你有一个非常专业的问题,你真正的意思是“任何事情”而不是“这些简短的事情之一”。

(值得注意的是,您调用了 append()。我认为在 Go 中写 append() 是不可能的。这就是为什么它必须是语言的一部分而不是 stdlib 函数。当我开始工作时Go,我认为这是该语言的一个重大缺陷。我在 Go 中工作的时间越长,我就越发现它并不重要。你只需编写代码并继续前进。)

【讨论】:

  • 这是针对这种情况的最佳答案。我不认为自己是通用程序员,而 C 是我的第一语言——当我看到机会时,我想在 Go 中尝试一下。我决定将一些派生类绑定到基类中,以消除一些样板文件并继续该项目。喜欢这种语言和它的大部分功能,但最好是亲吻并更接近事物的“Go Way”!感谢您提供有用的答案。
  • 这里的重要教训:Go 也不是面向对象的。因此,如果您正在考虑“类”,特别是“基”类,那么您也在与 Go 作斗争。 Go 是关于接口的,但它没有继承。也就是说,研究嵌入。这是大多数新手都没有意识到的关键 Go 功能,它完全改变了您构建类型的方式。 golang.org/doc/effective_go.html#embedding(也可以阅读所有“有效围棋”。这是对“围棋方式”的最佳介绍之一。)
  • 感谢阅读材料和指导,让我朝着更正确的方向前进。我将重新阅读/研究它以使其全部吸收。再次感谢您的努力,Rob!
【解决方案2】:

我查看了您的示例代码及其错误。要了解有关interface slice 的更多信息,请参阅此Go wikiSliceTricks


通用实现有点复杂,您可以使用基于类型的实现。然而,基于类型的实现在样板/重复上带来了更多代码。

您需要使用reflect 包来达到您的目的,正如评论中提到的@stephen-weinberg。

所以最好的起点是;试试这个库github.com/anzhihun/generic 并通过库代码库,自行实现。这个库使用反射。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-08
    • 2017-07-27
    • 2021-10-26
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-10
    相关资源
    最近更新 更多