【问题标题】:General slice type in golang?golang中的通用切片类型?
【发布时间】:2014-08-08 06:12:53
【问题描述】:

我在尝试将切片类型扩展为 Go 中的通用类型时遇到了一些困难。我创建了一个示例代码来解释我的问题。 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())
}

main() 函数的最后一行返回一个错误,指出 Sequencer 类型的变量不能被切片:

不能对 c 进行切片(类型 Sequencer)

有没有一种方法可以定义一个通用的切片类型(int、float64、string、...)而不隐藏切片的酷索引功能?

【问题讨论】:

    标签: types go slice


    【解决方案1】:

    当然。这与任何其他具有接口概念的语言没有什么不同。

    您试图在不支持的类型上调用“运算符”[] - Sequencer(一个接口)。而 Sequence 确实如此 - 因为它具有切片的属性,因此对 b.Meanb[:] 的调用起作用。

    例如,如果这是 C#,您实际上是在尝试:

    interface Sequencer {
        float Mean();
    }
    
    Sequencer c = ...;
    
    c[any_index] ... // error - interface doesn't have this operator defined
    

    这里的限制是你不能在 Go 中实现运算符重载。如果可以 - 那么您将能够将其添加到界面中,所有内容都会按预期进行。

    【讨论】:

    • 你的回答是,Go 中没有办法在切片中定义的“[]”上实现运算符重载?
    • 不..我的回答是你不能定义运算符..因此你的界面不能定义一个。当您尝试通过界面的“眼睛”访问变量时..它不会看到它。因此,您必须使用您的自定义 typedef,您可以使用 type Sequence []int 并以这种方式访问​​它。
    • 但是如果我事先不知道切片的类型怎么办?那我就不能索引一个通用的切片类型了?
    • 否 - 因为 Go 没有泛型。您将查看类型断言或打开类型来执行此操作.. 它会很快变得非常难看。不过可能有一种方法可以使用 reflect 包..
    【解决方案2】:

    提供接口定义中声明的方法的任何类型都可以存储在该类型的接口变量中。虽然您存储的实际值是一个切片,但任何类型都可以实现该接口。而且由于在许多情况下不可能静态确定接口变量的动态类型,因此该语言不允许您在没有显式类型断言的情况下窥视幕后。

    如果切片是您希望实现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

    【讨论】:

    • 我最喜欢这个提议,因为它可以在没有任何类型断言的情况下进行切片,但我仍然认为遗憾的是不能使用方便的索引样式 [:] 我们不需要知道切片尺寸...
    【解决方案3】:

    你有

    type Sequencer interface {
        Mean() float64
    }
    
    c := Sequencer(b)
    

    因此,变量c 包含满足Sequencer 接口的某种类型的值;该类型有一个Mean 方法。这就是我们所能说的,不多也不少。这并不意味着可以对变量c 值进行切片。因此,切片表达式c[:2] 无效。例如,我们可以定义一个满足Sequencer 接口但不能被切片的类型Map。如果要对c 进行切片,则断言它是可以切片的类型,例如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())
    }
    

    输出:

    2.5
    1.5
    2.5
    1.5
    2.929795
    

    【讨论】:

    • 是的 +1。我没有包含断言样本(我真的很懒:/)
    猜你喜欢
    • 2013-10-23
    • 2014-08-05
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-19
    相关资源
    最近更新 更多