Golang 相当于 itertools.chain?
Golang's equivalent of itertools.chain?
在 Golang 中,如何使用单个 for 循环迭代三个切片,而不创建包含所有三个切片中元素副本的新切片?有没有像 Python 的 itertools.chain?
您可以使用频道来构建您自己的 chain()
方法。
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chain(slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chain[T any](slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
channel <- val
}
}
close(channel)
}()
return channel
}
这里是link到Go Playground.
本质上,这个想法是使用可变参数 slices
,它可以保存和不确定数量的切片,然后在函数中我们创建一个给定类型的通道 return 该通道,以便调用者可以从中检索值,在我们这样做之前,我们启动一个 Goroutine,它实际上将通过通道发送值。为了能够使用 range
我们需要 close()
循环遍历所有切片后的通道。
这使用了从最近发布的 Go 1.18 开始可用的泛型。
编辑
正如 Zombo 所正确指出的那样,如果您 break
在上面的范围循环中,您将泄漏一个 goroutine,因为并非所有发送到通道的值都会从中检索。
为了在不泄露 go 例程的情况下获得 中断行为 你可以传递一个 predicate
函数,当它 returns true
将关闭通道,从而防止泄漏。
同样,您可以根据您的用例为各种东西(例如过滤)传递回调函数。
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chainUntil(func(item int) bool {
return item > 6
}, slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chainUntil[T any](predicate func(item T) bool, slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
if predicate(val) == true {
close(channel)
return
}
channel <- val
}
}
close(channel)
}()
return channel
}
预期输出:
2
3
5
使用泛型的简单解决方案
package main
import "fmt"
func Chain[T any](f func(e T), slices ...[]T) {
for _, slice := range slices {
for _, e := range slice {
f(e)
}
}
}
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{10, 20, 30}
slice3 := []int{100, 200, 300}
Chain(func(e int) {
fmt.Println(e)
}, slice1, slice2, slice3)
}
输出:
1
2
3
10
20
30
100
200
300
函数Chain
接受一个函数参数,将对每个切片中的每个元素连续执行。此函数将作为您的循环体代码。
解决方案可以扩展以保留其他循环功能,例如 break
和索引号:
func Chain[T any](f func(i int, e T) bool, slices ...[]T) {
var i int
for _, slice := range slices {
for _, e := range slice {
if !f(i, e) {
return
}
i++
}
}
}
...
Chain(func(i int, e int) bool {
fmt.Println(i, "-", e)
return (e <= 20)
}, slice1, slice2, slice3)
...
循环将在 f
returns false
时“中断”。
输出:
0 - 1
1 - 2
2 - 3
3 - 10
4 - 20
5 - 30
在 Golang 中,如何使用单个 for 循环迭代三个切片,而不创建包含所有三个切片中元素副本的新切片?有没有像 Python 的 itertools.chain?
您可以使用频道来构建您自己的 chain()
方法。
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chain(slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chain[T any](slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
channel <- val
}
}
close(channel)
}()
return channel
}
这里是link到Go Playground.
本质上,这个想法是使用可变参数 slices
,它可以保存和不确定数量的切片,然后在函数中我们创建一个给定类型的通道 return 该通道,以便调用者可以从中检索值,在我们这样做之前,我们启动一个 Goroutine,它实际上将通过通道发送值。为了能够使用 range
我们需要 close()
循环遍历所有切片后的通道。
这使用了从最近发布的 Go 1.18 开始可用的泛型。
编辑
正如 Zombo 所正确指出的那样,如果您 break
在上面的范围循环中,您将泄漏一个 goroutine,因为并非所有发送到通道的值都会从中检索。
为了在不泄露 go 例程的情况下获得 中断行为 你可以传递一个 predicate
函数,当它 returns true
将关闭通道,从而防止泄漏。
同样,您可以根据您的用例为各种东西(例如过滤)传递回调函数。
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chainUntil(func(item int) bool {
return item > 6
}, slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chainUntil[T any](predicate func(item T) bool, slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
if predicate(val) == true {
close(channel)
return
}
channel <- val
}
}
close(channel)
}()
return channel
}
预期输出:
2
3
5
使用泛型的简单解决方案
package main
import "fmt"
func Chain[T any](f func(e T), slices ...[]T) {
for _, slice := range slices {
for _, e := range slice {
f(e)
}
}
}
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{10, 20, 30}
slice3 := []int{100, 200, 300}
Chain(func(e int) {
fmt.Println(e)
}, slice1, slice2, slice3)
}
输出:
1
2
3
10
20
30
100
200
300
函数Chain
接受一个函数参数,将对每个切片中的每个元素连续执行。此函数将作为您的循环体代码。
解决方案可以扩展以保留其他循环功能,例如 break
和索引号:
func Chain[T any](f func(i int, e T) bool, slices ...[]T) {
var i int
for _, slice := range slices {
for _, e := range slice {
if !f(i, e) {
return
}
i++
}
}
}
...
Chain(func(i int, e int) bool {
fmt.Println(i, "-", e)
return (e <= 20)
}, slice1, slice2, slice3)
...
循环将在 f
returns false
时“中断”。
输出:
0 - 1
1 - 2
2 - 3
3 - 10
4 - 20
5 - 30