如何解决 Go 没有参数多态性的问题?
我是一个 Go 新手,但我读到 Go 常客不会错过参数多态性。每次我尝试学习一门新语言时,我都会使用 L99问题列表以获得一些练习。
即使我尝试编写像第一个问题一样简单的东西(在 Go 中将是一个单独的语句,获取切片的最后一个元素),我如何将其编写为一个函数,该函数获取任何类型的切片并且(使用我上面引用的单个语句)返回该切片的最后一个元素?
我认为即使该语言没有参数多态性,也必须有一些惯用的“Go”方式来做到这一点,以便 Go 常规者声称他们不会错过参数多态性。否则,如果示例比列表的最后一个元素更复杂,则您将需要一个函数来为每种类型执行任务。
我缺少什么?
I'm a Go newcomer, but I have read that Go regulars do not miss parametric polymorphism. Every time I try to learn a new language I use the L99 list of problems to get some practice.
Even if I try to write something as trivial as the first problem (which in Go would be a single statement, taking the last element of a slice), how would I write this as a function that takes a slice of any type and (using that single statement I referenced above) returns the last element of that slice?
I figured even though the language does not have parametric polymorphism there must be some idiomatic 'Go' way of doing this in order for Go regulars to claim they dont miss parametric polymorphism. Otherwise, if the example were more complex than just the last element of a list for instance, you would need a function to perform your task for every type.
What am I missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(4)
这听起来很像当我发现我用其他编程语言(如 C、fpc 或 delphi)为不同类型的不同数组多次编写相同的代码时。我为一种可能永远不会实现的语言发明了参数多态性,使用预处理器技巧并将其称为“包含文件参数多态性”作为概念证明,您实际上可以将参数多态性实现到过程语言中,而不需要 OOP 或任何复杂的东西仿制药系统。使用预处理器虽然是一种滥用,但这只是为了用 FPC 来证明这个概念。
由于 Golang 不使用预处理器,因此您必须使用接口或指针并将类型作为参数发送。但即使使用指针仍然意味着您必须编写大量代码来转换它并使其全部工作。接口比指针更好,因为指针不太安全。
像这样的解决方案:
last := a[len(a)-1]
容易出现错误,因为有人可能会忘记负数1。有些语言有一些更好的东西:
// return last element, the "high" of a
last := a[high(a)]
// return first element, the "low" of a
first := a[low(a)]
上面的代码在 Go AFAIK 中不起作用(还没有研究 go 是否有类似的东西),这只是一些其他语言有(fpc),Go 可能会考虑这一点。
这种处理事物的低和高方式绝对确保选择最后一个和第一个元素,而使用“减一”很容易出现基本的数学错误。有人可能会忘记负一...因为他们对基于 1 的数组和基于零的数组感到困惑。即使该语言没有基于 1 的数组之类的东西,人们仍然可能会犯错误,因为人类有时会以基于 1 的方式思考(我们的手指从 1 开始,而不是 0)。一些聪明的程序员会争辩说,不,我们的手指从零开始,而不是从一开始。你的拇指为零。好吧,好吧……但是……对于世界上的大多数人来说……;-)我们最终会在现实世界和计算机世界中整天从 1 基到 0 基来来回切换我们的大脑,这会导致许多软件中的错误。
但有些人会认为“低”和“高”只是语法糖,在极简语言中不是必需的。必须确定额外的安全是否值得,在许多情况下是值得的。我不确定 LOW() 和 HIGH() 会给编译器增加多少复杂性,以及它如何影响性能..我不是 100% 确定...我认为编译器可以智能地优化高和低,但我不确定。
只是回答如何获取数组的最后一个(和第一个)元素的问题,这是 Go 中的正确方法:
最后 := a[:1]
第一个 := a[1:]
但这与参数多态性无关,参数多态性是类型推断,是在编译时计算的。
我正在尝试编写二叉树库,并且仍在努力寻找最有效、可读和高性能的方法来抽象数据类型,具体来说,我编写了存储、游标和索引映射系统,并且walk 函数,但我希望能够切换实际存储在节点中的数据类型。我在这个过程中学到了很多关于组合和嵌入的知识,但这并没有让我完全高兴。
我确实了解一些函数式编程的原理,并且 Go 恰好将函数视为一流的,因此理论上可能存在参数多态性问题的函数式解决方案。我正在弄清楚它,因为基本上我喜欢函数范式,但无论如何我讨厌递归(我更喜欢迭代,对我来说更容易想象 100 倍)。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
你引用了“99 lisp 问题”,但 Lisp 根本没有参数多态性或静态类型。
许多静态类型语言,如 Objective-C 和泛型之前的 Java,没有参数多态性。解决方案是只使用可以接受所有值的类型,在 Go 中是
interface{}
,并在需要从中获取某些特定类型时进行强制转换。对于您的具体问题,如何采取“任何类型的切片”;不幸的是,没有专门包含切片的接口,因为切片没有任何方法;因此您将不得不使用
interface{}
。由于切片类型未知,因此需要使用反射(reflect
包)来执行所有切片操作,包括获取长度和容量、附加以及访问特定索引处的元素。另一种选择是,在所有代码中不使用“任何类型的切片”,而只使用“interface{} 的切片”,即
[]interface{}
,然后您可以使用普通的切片运算符在上面,你可以放入任何元素,但当你取出它们时就会进行投射。You cite the "99 lisp problems", yet Lisp does not have parametric polymorphism or static types at all.
Many statically-typed languages, like Objective-C, and Java before generics, have no parametric polymorphism. The solution is to just use a type that can accept all values, which in Go is
interface{}
, and cast when you need to get some specific type out of it.For your specific question, how to take "any type of slice"; unfortunately, there is no interface that includes specifically slices, since slices do not have any methods; so you'll be stuck with using
interface{}
. Since you have an unknown slice type, you need to use reflection (thereflect
package) to perform all the slice operations, including getting the length and capacity, appending, and accessing the element at a particular index.Another alternative is that instead of using "slice of any type", just use "slice of interface{}" i.e.
[]interface{}
, in all your code, then you can use the normal slice operators on it, and you can put any elements in, but cast when you get them out.