General slice type in golang?

huangapple go评论84阅读模式
英文:

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和两个类型SequenceMap,它们都实现了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.Meanb[:]的调用有效。

举个例子,如果这是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.

huangapple
  • 本文由 发表于 2014年6月18日 12:47:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/24277248.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定