【问题标题】:Pass list of one of two structures to the function将两个结构之一的列表传递给函数
【发布时间】:2023-01-20 00:44:47
【问题描述】:

Go 中的新功能,找不到任何直观的方法。

我有这样一段代码

tx = getTx()
for _, record := range tx.a {
    // do a lot with record.Important
}
for _, record := range tx.b {
    // do a lot with record.Important
}
for _, record := range tx.c {
    // do a lot with record.Important
}

以及以下结构:

type Record1 struct {
    // fields of Record1
    Important string
}
type Record2 struct {
    // fields of Record1
    Important string
}
type TX struct {
    a []Record1
    b []Record1
    c []Record2
}

现在合乎逻辑的是提取每个为了函数逻辑:

func helper(records) { // Here is the problem
   // do a lot with record.Important
}

问题:

记录[]Record1[]Record2 类型。但看起来 Golang 中不存在 Union 类型。所以我想我可以将[]string 传递给帮手,但甚至无法找到一种优雅的方式来获得与 map(lambda r: r.Important, tx.a) 等同的东西。没有高阶地图功能,列表理解。我不相信使用原始为了循环来解决这个问题。

【问题讨论】:

  • 使用接口或泛型或反射。该语言的名称是“Go”。
  • 您应该阅读有关接口和组合的内容。

标签: list go struct union


【解决方案1】:

跨多种类型进行循环的一种方法是将接口与泛型一起使用。让每个 Record 类型为重要字段实现一个 getter 方法。然后声明一个接口,在其方法集中包含该 getter 方法。然后,您可以通过将接口声明为其类型参数来使您的helper 通用。

func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }

type ImportantGetter interface {
     GetImporant() string
}

func helper[T ImportantGetter](s []T) {
    for _, v := range s {
        _ = v.GetImportant()
    }
}

【讨论】:

  • 不应该有return r.Important吗?
  • 是的,先生,非常喜欢!固定的。 @kosciej16
【解决方案2】:

除非我误解了你的问题,否则你似乎想从一组记录中提取 X 列中的所有值,然后将这些值作为切片传递给某个函数——我的假设是基于你的愿望有类似map()的东西。

如果您追求的是类型不可知论,那么您当然可以使用 mkopriva 建议的接口方法,但您不会摆脱使用 for 循环 - 列表类型的迭代是核心惯用的去。如果你需要一个映射函数,你将不得不编写一个执行你想要的映射的函数。

我注意到你不需要泛型来做mkopriva建议的事情,你可以只使用一个接口而不用泛型go playground混淆水域:

package main

import "fmt"

type Record1 struct {
    Important string
}

type Record2 struct {
    Important string
}

func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }

type ImportantGetter interface {
    GetImportant() string
}

func helper(s []ImportantGetter) {
    for _, v := range s {
        fmt.Println(v.GetImportant())
    }
}

func main() {
    records := []ImportantGetter{Record1{Important: "foo"}, Record2{Important: "bar"}}

    helper(records)
}

类型不可知论的另一种方法,以及“我希望所有这些类型都具有共同的属性”的惯用方法(恕我直言)是使用 struct 嵌入和类型断言来构建您自己的 Map()功能起来go playground

type CommonFields struct {
    Important string
}

type Record1 struct {
    CommonFields
    FieldSpecificToRecord1 string
}

type Record2 struct {
    CommonFields
    FieldSpecificToRecord2 int
}

func main() {
    r1 := Record1{
        CommonFields{Important: "I'm r1!"},
        "foo",
    }

    r2 := Record2{
        CommonFields{Important: "I'm r2!"},
        5,
    }

    records := []interface{}{r1, r2, "this is not a valid record type"}

    fmt.Println(Map(records))

}

func Map(source []interface{}) []string {
    destination := make([]string, len(source))
    for i, sourceRecord := range source {
        if rr, ok := sourceRecord.(Record1); ok {
            destination[i] = rr.Important
        } else if rr, ok := sourceRecord.(Record2); ok {
            destination[i] = rr.Important
        } else {
            destination[i] = "undefined"
        }
    }
    return destination
}

您可能想让 Map() 的实现接受一个参数,该参数指定要提取的字段以符合您在其他语言中的内容,或者甚至可能只是传入一个辅助函数来完成大部分特定于类型的值提取.

【讨论】:

    猜你喜欢
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多