在Go语言中,根据条件从切片中选择一个值的最惯用方式是什么?

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

What's the most idiomatic way to pick a value from a slice based on a condition in Go?

问题

我一直在使用循环来过滤数值,有时甚至将切片转换为具有方便索引的映射,以实现这一目的,但我觉得这可能不是在Go中做这件事的最佳方式。所以:

考虑以下样板代码,找到people切片中第一个名为"Bob"的人,最符合惯用方式的是什么?found应该是一个布尔变量,用于确定是否找到了名为"Bob"的人。

package main

import "fmt"

type Person struct {
    Age  int
    Name string
}

var people = []Person{{Age: 18, Name: "Deb"}, {Age: 22, Name: "Bob"}, {Age: 35, Name: "Bob"}}

func main() {
    FirstBob, found := // 如何选择第一个"Bob"?
    fmt.Println(FirstBob)
}

更一般地说:选择满足特定条件的切片中的第一个值的最惯用方式是什么?

英文:

I've been doing a lot of loops to filter values and sometimes even converted slices to maps with convenient indexes to achieve this, but I feel it might not be the best way to do it in Go. So:

Considering the following boilerplate code, what's the most idiomatic way to find the first person named "Bob" on people slice? found should be a bool variables to determine if a person named "Bob" was found or not.

package main

import "fmt"

type Person struct {
    Age int
    Name string
}

var people=[]Person{{Age:18,Name:"Deb"},{Age:22,Name:"Bob"},{Age:35,Name:"Bob"}}

func main() {
	FirstBob, found := // how to pick first "Bob"?
	fmt.Println(FirstBob)
}

Being more general: What's the most idiomatic way to pick the first value from a slice that matches a certain condition?

答案1

得分: 2

例如,

package main

import "fmt"

type Person struct {
    Age  int
    Name string
}

var people = []Person{{Age: 18, Name: "Deb"}, {Age: 22, Name: "Bob"}, {Age: 35, Name: "Bob"}}

func findPerson(people []Person, name string) (Person, bool) {
    for _, p := range people {
        if p.Name == name {
            return p, true
        }
    }
    return Person{}, false
}

func main() {
    FirstBob, found := findPerson(people, "Bob")
    if found {
        fmt.Println(FirstBob)
    }
}

输出:

{22 Bob}

如果你想返回指向 Person 结构体的指针,

package main

import "fmt"

type Person struct {
    Age  int
    Name string
}

var people = []Person{{Age: 18, Name: "Deb"}, {Age: 22, Name: "Bob"}, {Age: 35, Name: "Bob"}}

func findPerson(people []Person, name string) (*Person, bool) {
    for i := range people {
        p := &people[i]
        if p.Name == name {
            return p, true
        }
    }
    return nil, false
}

func main() {
    FirstBob, found := findPerson(people, "Bob")
    if found {
        fmt.Println(*FirstBob)
    }
}

输出:

{22 Bob}
英文:

For example,

package main

import "fmt"

type Person struct {
	Age  int
	Name string
}

var people = []Person{{Age: 18, Name: "Deb"}, {Age: 22, Name: "Bob"}, {Age: 35, Name: "Bob"}}

func findPerson(people []Person, name string) (Person, bool) {
	for _, p := range people {
		if p.Name == name {
			return p, true
		}
	}
	return Person{}, false
}

func main() {
	FirstBob, found := findPerson(people, "Bob")
	if found {
		fmt.Println(FirstBob)
	}
}

Output:

{22 Bob}

If you want to return a pointer to the Person struct,

package main

import "fmt"

type Person struct {
	Age  int
	Name string
}

var people = []Person{{Age: 18, Name: "Deb"}, {Age: 22, Name: "Bob"}, {Age: 35, Name: "Bob"}}

func findPerson(people []Person, name string) (*Person, bool) {
	for i := range people {
		p := &people[i]
		if p.Name == name {
			return p, true
		}
	}
	return nil, false
}

func main() {
	FirstBob, found := findPerson(people, "Bob")
	if found {
		fmt.Println(*FirstBob)
	}
}

Output:

{22 Bob}

答案2

得分: 2

惯用的方式是写出循环,例如,Go团队成员(以前是Python编码员)Andrew Gerrand在2014年GopherCon的闭幕演讲中提到了其他函数式列表处理快捷方式("filter"、"map"等),链接为https://www.youtube.com/watch?v=dKGmK_Z1Zl0&t=23m40s

标准的理由是不使用这些快捷方式可以产生更一致的代码(开发人员不能在是否使用map上做出不同选择),而且它是显式的(如果循环可见作为for块,当你进行嵌套循环或对大型数据集进行循环时,更容易理解)。

话虽如此,1)如果你在很多地方重复相同类型的搜索,最好像peterSO的答案中那样将其因式分解,2)"惯用"是一个并非所有代码都始终满足的理想;如果你认为这样可以使你的代码更容易处理,那么你可以编写一个特定类型的快捷方式(思考:type People []Person; func (p People) FirstMatchingIndex(predicate func(p *Person) bool) int {...})。例如,标准的strings.IndexFunc函数接近于一个"查找(满足谓词的)第一个项目的索引"函数。

英文:

The idiomatic way is to write out the loop--for example, Go team member (and former Python coder) Andrew Gerrand's closing talk at GopherCon 2014 mentions other functional-style list-processing shortcuts ("filter", "map", etc.) at https://www.youtube.com/watch?v=dKGmK_Z1Zl0&t=23m40s

The standard justification is that not using those shortcuts yields more consistent code (developers can't make different choices about whether to use map or not), and it's explicit (it's held to be more obvious when you're doing nested loops or loops over enormous datasets if the loops are visible as a for block).

That said, 1) if you repeat the same kind of search lots of places you might as well factor it like in peterSO's answer, and 2) "idiomatic" is an ideal that not all code satisfies all the time; if you think it makes your code vastly easier to work with, nothing stops you from writing a type-specific shortcut (think: type People []Person; func (p People) FirstMatchingIndex(predicate func(p *Person) bool) int {...}). The standard strings.IndexFunc is close to a "find (the index of) the first item satisfying a predicate" function, for example.

huangapple
  • 本文由 发表于 2014年11月30日 09:36:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/27209100.html
匿名

发表评论

匿名网友

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

确定