将一个由两种结构之一组成的列表传递给函数。

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

Pass list of one of two structures to the function

问题

在Go语言中,我找不到任何直观的方法来实现这个。

我有这样一段代码:

tx := getTx()
for _, record := range tx.a {
    // 对record.Important进行很多操作
}
for _, record := range tx.b {
    // 对record.Important进行很多操作
}
for _, record := range tx.c {
    // 对record.Important进行很多操作
}

还有以下结构体:

type Record1 struct {
    // Record1的字段
    Important string
}
type Record2 struct {
    // Record2的字段
    Important string
}
type TX struct {
    a []Record1
    b []Record1
    c []Record2
}

现在的逻辑是将每个for循环逻辑提取到函数中:

func helper(records) { // 这里是问题所在
   // 对record.Important进行很多操作
}

问题:

records[]Record1[]Record2类型。但是在Golang中似乎不存在联合类型。所以我想将[]string传递给helper,但是甚至找不到一个优雅的方法来实现类似于map(lambda r: r.Important, tx.a)的功能。没有高阶map函数,也没有列表推导式。我不确定是否应该使用原始的for循环来解决这个问题。

英文:

New in Go, couldn't find any intuitive way of doing that.

I have such piece of code

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
}

And the following structs:

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
}

Now the logical is to extract every for logic into the function:

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

Problem:

records is a []Record1 or []Record2 type. But it looks like Union types doesn't exists in Golang. So I thought I could pass []string into the helper, but cannot even find an elegant way to get something equivalent to map(lambda r: r.Important, tx.a). There is no high order map function, no list comprehesion. I am not convinced to use raw for loop to solve that.

答案1

得分: 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()
    }
}
英文:

One approach to do the loop across multiple types is to use interfaces together with generics. Have each Record type implement a getter method for the important field. Then declare an interface that includes that getter method in its method set. Then you can make your helper generic by declaring the interface as its type parameter.

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()
    }
}

答案2

得分: 0

除非我误解了你的问题,否则看起来你想从一组记录中提取列X的所有值,然后将这些值作为切片传递给某个函数 - 我的假设基于你希望Go语言有类似于map()的功能。

如果你追求的是类型无关性,你可以使用像mkopriva建议的接口方法,但你无法避免使用for循环 - 在Go语言中,对列表类型进行迭代是核心的惯用方法。如果你需要一个映射函数,你需要编写一个执行你想要的映射的函数。

我要注意的是,你不需要泛型来实现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()函数的实现接受一个参数来指定要提取的字段,以符合你在其他语言中的做法,或者甚至只需传入一个辅助函数,该函数执行大部分特定类型的值提取。

英文:

Unless I'm misunderstanding your question, it seems like you want to extract all the values in column X from a set of records and then pass those values in as a slice to some function - I'm basing my assumption on your wish that go had something like map().

If what you're after is type-agnosticism, you could certainly use an interface approach like that suggested by mkopriva, but you aren't going to get out of using a for loop - iteration over list types is core to idiomatic go. If you need a mapping function, you're going to have to write one that performs the mapping you want.

I'd note that you do not need generics to do what mkopriva suggests, you can just use an interface without muddying the waters with generics 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)
}

Another approach to the type-agnosticism, and one that's a bit more (IMHO) idiomatic for "I expect all of these types to have a common property," is to use struct embedding and type assertions to build your own Map() function up 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
}

You'd likely want to make your implementation of Map() accept an argument specifying the field to extract to conform to what you have in other languages, or possibly even just pass in a helper function which does most of the type-specific value extraction.

huangapple
  • 本文由 发表于 2023年1月19日 18:15:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75170695.html
匿名

发表评论

匿名网友

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

确定