英文:
General slice type in golang?
问题
我在尝试将切片类型扩展为Go中的通用类型时遇到了一些困难。我创建了一个示例代码来解释我的问题。play ground版本
package main
import "fmt"
type Sequencer interface {
Mean() float64
}
type Sequence []int
func (s Sequence) Mean() float64 {
sum := 0.0
for _, el := range s {
sum += float64(el)
}
return sum / float64(len(s))
}
func main() {
a := []int{1, 2, 3, 4}
b := Sequence(a)
fmt.Println(b.Mean())
fmt.Println(b[:2].Mean())
c := Sequencer(b)
fmt.Println(c.Mean())
fmt.Println(c[:2].Mean())
}
main() 函数的最后一行返回一个错误,指出类型为 Sequencer 的变量无法进行切片操作:
cannot slice c (type Sequencer)
是否有一种方法可以定义切片的通用类型(int、float64、string 等),而不隐藏切片的索引功能呢?
英文:
I'm having some difficulties trying to extend slice types into a general type in Go. I have created a sample code to explain my problem. play ground version
package main
import "fmt"
type Sequencer interface {
Mean() float64
}
type Sequence []int
func (s Sequence) Mean() float64 {
sum := 0.0
for _, el := range s {
sum += float64(el)
}
return sum / float64(len(s))
}
func main() {
a := []int{1, 2, 3, 4}
b := Sequence(a)
fmt.Println(b.Mean())
fmt.Println(b[:2].Mean())
c := Sequencer(b)
fmt.Println(c.Mean())
fmt.Println(c[:2].Mean())
}
Last line of the main() function returns an error saying that variables of type Sequencer cannot be sliced:
> cannot slice c (type Sequencer)
Is there a way of defining a general type of slices (int, float64, string,...) without hiding the cool indexing capabilities of slices?
答案1
得分: 5
你有一个代码片段,其中定义了一个接口Sequencer
和两个类型Sequence
和Map
,它们都实现了Sequencer
接口的Mean
方法。在main
函数中,你创建了一个Sequence
类型的变量b
,并调用了它的Mean
方法。然后,你尝试对b
进行切片操作b[:2]
,但这是无效的,因为接口类型不能被切片。如果你想对b
进行切片操作,你需要断言它的具体类型为Sequence
,例如c.(Sequence)[:2]
。最后,你还创建了一个Map
类型的变量m
,并调用了它的Mean
方法。
输出结果为:
2.5
1.5
2.5
1.5
2.929795
英文:
You have
type Sequencer interface {
Mean() float64
}
c := Sequencer(b)
Therefore, the variable c
contains a value of some type which satisfies the Sequencer
interface; the type has a Mean
method. That's all we can say, no more, no less. It does not imply that the variable c
value can be sliced. Therefore, the slice expression c[:2]
is invalid. For example, we could define a type Map
which satisfies the Sequencer
interface but cannot be sliced. If you want to slice c
then assert that it is of a type that can be sliced, for example, c.(Sequence)[:2]
.
package main
import "fmt"
type Sequencer interface {
Mean() float64
}
type Sequence []int
func (s Sequence) Mean() float64 {
sum := 0.0
for _, el := range s {
sum += float64(el)
}
return sum / float64(len(s))
}
type Map map[string]float64
func (m Map) Mean() float64 {
sum := 0.0
for _, v := range m {
sum += float64(v)
}
return sum / float64(len(m))
}
func main() {
a := []int{1, 2, 3, 4}
b := Sequence(a)
fmt.Println(b.Mean())
fmt.Println(b[:2].Mean())
c := Sequencer(b)
fmt.Println(c.Mean())
fmt.Println(c.(Sequence)[:2].Mean())
m := Map{"one": 3.14159, "two": 2.718}
fmt.Println(m.Mean())
}
Output:
2.5
1.5
2.5
1.5
2.929795
答案2
得分: 4
任何提供接口定义中声明的方法的类型都可以存储在该类型的接口变量中。虽然你存储的实际值是一个切片,但任何类型都可以实现该接口。由于在许多情况下无法静态确定接口变量的动态类型,所以语言不允许你在没有显式类型断言的情况下查看底层内容。
如果你期望实现Sequencer
类型的类型实现切片操作,那么简单的解决方案是扩展接口以包含该方法:
type Sequencer interface {
Mean() float64
Slice(start, end int) Sequencer
}
可以按照明显的方式为Sequence
类型实现该方法:
func (s Sequence) Slice(start, end int) Sequencer {
return s[start:end]
}
然后可以使用该方法获取切片的平均值:
fmt.Println(c.Slice(0, 2).Mean())
你可以在这里尝试这个解决方案:http://play.golang.org/p/UMuqOarLUu
英文:
Any type that provides the methods declared in an interface definition can be stored in an interface variable of that type. While the actual value you are storing is a slice, any type could implement the interface. And since in many cases it is impossible to statically determine the dynamic type of an interface variable, the language doesn't let you peek below the covers without an explicit type assertion.
If slicing is something you expect types implementing your Sequencer
type to implement, then the simple solution is to extend the interface to include such a method:
type Sequencer interface {
Mean() float64
Slice(start, end int) Sequencer
}
This can be implemented for your Sequence
type in the obvious way:
func (s Sequence) Slice(start, end int) Sequencer {
return s[start:end]
}
You can then get the mean of a slice using the method:
fmt.Println(c.Slice(0, 2).Mean())
You can experiment with this solution here: http://play.golang.org/p/UMuqOarLUu
答案3
得分: 2
当然可以。这与任何其他具有接口概念的语言没有区别。
你试图在一个不支持的类型Sequencer
(一个接口)上调用[]
运算符。而Sequence
支持,因为它具有切片的属性,所以b.Mean
和b[:]
的调用有效。
举个例子,如果这是C#,你实际上是在尝试这样做:
interface Sequencer {
float Mean();
}
Sequencer c = ...;
c[any_index] ... // 错误 - 接口没有定义这个运算符
这里的限制是在Go中无法实现运算符重载。如果可以的话,你只需将其添加到接口中,一切都会按预期进行。
英文:
Of course. This is no different from any other language that has the concept of an interface.
You're trying to call the "operator" []
on a type that doesn't support it - Sequencer
(an interface). Whereas, Sequence
does - because it takes on the properties of a slice, hence why the calls to b.Mean
and b[:]
work.
If this were C# for example, you're essentially trying this:
interface Sequencer {
float Mean();
}
Sequencer c = ...;
c[any_index] ... // error - interface doesn't have this operator defined
The limitation here is that you cannot implement operator overloads in Go. If you could - then you would be able to just add that to the interface and all would tick along as expected.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论