如何在Go语言中将切片组合成元组切片(实现Python的`zip`函数)?

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

How to combine slices into a slice of tuples in Go (implementing python `zip` function)?

问题

有时候,在Python中可以使用zip内置函数将两个列表合并成一个元组。那么在Go语言中如何实现类似的功能呢?

例如:

  1. package main
  2. import "fmt"
  3. func main() {
  4. list1 := []int{1, 2}
  5. list2 := []int{3, 4}
  6. result := make([][2]int, len(list1))
  7. for i := range list1 {
  8. result[i] = [2]int{list1[i], list2[i]}
  9. }
  10. fmt.Println(result)
  11. }

这段代码将输出:

  1. [[1 3] [2 4]]

在Go语言中,我们可以使用循环来遍历两个列表,并将对应位置的元素组合成一个新的元组。

英文:

Sometimes, it's convenient to combine two lists into a tuple using zip built-in function in Python. How to make this similarly in Go?

For example:

  1. >>> zip ([1,2],[3,4])
  2. [(1,3), (2,4)]

答案1

得分: 19

你可以像这样做,给元组类型起一个名字:

  1. package main
  2. import "fmt"
  3. type intTuple struct {
  4. a, b int
  5. }
  6. func zip(a, b []int) ([]intTuple, error) {
  7. if len(a) != len(b) {
  8. return nil, fmt.Errorf("zip: arguments must be of same length")
  9. }
  10. r := make([]intTuple, len(a), len(a))
  11. for i, e := range a {
  12. r[i] = intTuple{e, b[i]}
  13. }
  14. return r, nil
  15. }
  16. func main() {
  17. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  18. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  19. fmt.Println(zip(a, b))
  20. }

或者可以使用无名类型作为元组,像这样:

  1. package main
  2. import "fmt"
  3. func zip(a, b []int) ([][2]int, error) {
  4. if len(a) != len(b) {
  5. return nil, fmt.Errorf("zip: arguments must be of same length")
  6. }
  7. r := make([][2]int, len(a), len(a))
  8. for i, e := range a {
  9. r[i] = [2]int{e, b[i]}
  10. }
  11. return r, nil
  12. }
  13. func main() {
  14. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  15. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  16. fmt.Println(zip(a, b))
  17. }

最后,这是一种软通用的方法:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func zip(a, b, c interface{}) error {
  7. ta, tb, tc := reflect.TypeOf(a), reflect.TypeOf(b), reflect.TypeOf(c)
  8. if ta.Kind() != reflect.Slice || tb.Kind() != reflect.Slice || ta != tb {
  9. return fmt.Errorf("zip: first two arguments must be slices of the same type")
  10. }
  11. if tc.Kind() != reflect.Ptr {
  12. return fmt.Errorf("zip: third argument must be pointer to slice")
  13. }
  14. for tc.Kind() == reflect.Ptr {
  15. tc = tc.Elem()
  16. }
  17. if tc.Kind() != reflect.Slice {
  18. return fmt.Errorf("zip: third argument must be pointer to slice")
  19. }
  20. eta, _, etc := ta.Elem(), tb.Elem(), tc.Elem()
  21. if etc.Kind() != reflect.Array || etc.Len() != 2 {
  22. return fmt.Errorf("zip: third argument's elements must be an array of length 2")
  23. }
  24. if etc.Elem() != eta {
  25. return fmt.Errorf("zip: third argument's elements must be an array of elements of the same type that the first two arguments are slices of")
  26. }
  27. va, vb, vc := reflect.ValueOf(a), reflect.ValueOf(b), reflect.ValueOf(c)
  28. for vc.Kind() == reflect.Ptr {
  29. vc = vc.Elem()
  30. }
  31. if va.Len() != vb.Len() {
  32. return fmt.Errorf("zip: first two arguments must have same length")
  33. }
  34. for i := 0; i < va.Len(); i++ {
  35. ea, eb := va.Index(i), vb.Index(i)
  36. tt := reflect.New(etc).Elem()
  37. tt.Index(0).Set(ea)
  38. tt.Index(1).Set(eb)
  39. vc.Set(reflect.Append(vc, tt))
  40. }
  41. return nil
  42. }
  43. func main() {
  44. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  45. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  46. c := [][2]int{}
  47. e := zip(a, b, &c)
  48. if e != nil {
  49. fmt.Println(e)
  50. return
  51. }
  52. fmt.Println(c)
  53. }
英文:

You could do something like this, where you give the tuple type a name:

  1. package main
  2. import &quot;fmt&quot;
  3. type intTuple struct {
  4. a, b int
  5. }
  6. func zip(a, b []int) ([]intTuple, error) {
  7. if len(a) != len(b) {
  8. return nil, fmt.Errorf(&quot;zip: arguments must be of same length&quot;)
  9. }
  10. r := make([]intTuple, len(a), len(a))
  11. for i, e := range a {
  12. r[i] = intTuple{e, b[i]}
  13. }
  14. return r, nil
  15. }
  16. func main() {
  17. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  18. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  19. fmt.Println(zip(a, b))
  20. }

Or alternatively use an unnamed type for the tuple, like this:

  1. package main
  2. import &quot;fmt&quot;
  3. func zip(a, b []int) ([][3]int, error) {
  4. if len(a) != len(b) {
  5. return nil, fmt.Errorf(&quot;zip: arguments must be of same length&quot;)
  6. }
  7. r := make([][4]int, len(a), len(a))
  8. for i, e := range a {
  9. r[i] = [2]int{e, b[i]}
  10. }
  11. return r, nil
  12. }
  13. func main() {
  14. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  15. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  16. fmt.Println(zip(a, b))
  17. }

And finally here's a soft-generic way of doing it:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. func zip(a, b, c interface{}) error {
  7. ta, tb, tc := reflect.TypeOf(a), reflect.TypeOf(b), reflect.TypeOf(c)
  8. if ta.Kind() != reflect.Slice || tb.Kind() != reflect.Slice || ta != tb {
  9. return fmt.Errorf(&quot;zip: first two arguments must be slices of the same type&quot;)
  10. }
  11. if tc.Kind() != reflect.Ptr {
  12. return fmt.Errorf(&quot;zip: third argument must be pointer to slice&quot;)
  13. }
  14. for tc.Kind() == reflect.Ptr {
  15. tc = tc.Elem()
  16. }
  17. if tc.Kind() != reflect.Slice {
  18. return fmt.Errorf(&quot;zip: third argument must be pointer to slice&quot;)
  19. }
  20. eta, _, etc := ta.Elem(), tb.Elem(), tc.Elem()
  21. if etc.Kind() != reflect.Array || etc.Len() != 2 {
  22. return fmt.Errorf(&quot;zip: third argument&#39;s elements must be an array of length 2&quot;)
  23. }
  24. if etc.Elem() != eta {
  25. return fmt.Errorf(&quot;zip: third argument&#39;s elements must be an array of elements of the same type that the first two arguments are slices of&quot;)
  26. }
  27. va, vb, vc := reflect.ValueOf(a), reflect.ValueOf(b), reflect.ValueOf(c)
  28. for vc.Kind() == reflect.Ptr {
  29. vc = vc.Elem()
  30. }
  31. if va.Len() != vb.Len() {
  32. return fmt.Errorf(&quot;zip: first two arguments must have same length&quot;)
  33. }
  34. for i := 0; i &lt; va.Len(); i++ {
  35. ea, eb := va.Index(i), vb.Index(i)
  36. tt := reflect.New(etc).Elem()
  37. tt.Index(0).Set(ea)
  38. tt.Index(1).Set(eb)
  39. vc.Set(reflect.Append(vc, tt))
  40. }
  41. return nil
  42. }
  43. func main() {
  44. a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
  45. b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
  46. c := [][2]int{}
  47. e := zip(a, b, &amp;c)
  48. if e != nil {
  49. fmt.Println(e)
  50. return
  51. }
  52. fmt.Println(c)
  53. }

答案2

得分: 14

要将一些[]int切片列表进行zip操作,

  1. package main
  2. import "fmt"
  3. func zip(lists ...[]int) func() []int {
  4. zip := make([]int, len(lists))
  5. i := 0
  6. return func() []int {
  7. for j := range lists {
  8. if i >= len(lists[j]) {
  9. return nil
  10. }
  11. zip[j] = lists[j][i]
  12. }
  13. i++
  14. return zip
  15. }
  16. }
  17. func main() {
  18. a := []int{1, 2, 3}
  19. b := []int{4, 5, 6}
  20. c := []int{7, 8, 9, 0}
  21. iter := zip(a, b, c)
  22. for tuple := iter(); tuple != nil; tuple = iter() {
  23. fmt.Println("tuple:", tuple)
  24. }
  25. }

输出结果:

  1. tuple: [1 4 7]
  2. tuple: [2 5 8]
  3. tuple: [3 6 9]
英文:

To zip some number of slice []int lists,

  1. package main
  2. import &quot;fmt&quot;
  3. func zip(lists ...[]int) func() []int {
  4. zip := make([]int, len(lists))
  5. i := 0
  6. return func() []int {
  7. for j := range lists {
  8. if i &gt;= len(lists[j]) {
  9. return nil
  10. }
  11. zip[j] = lists[j][i]
  12. }
  13. i++
  14. return zip
  15. }
  16. }
  17. func main() {
  18. a := []int{1, 2, 3}
  19. b := []int{4, 5, 6}
  20. c := []int{7, 8, 9, 0}
  21. iter := zip(a, b, c)
  22. for tuple := iter(); tuple != nil; tuple = iter() {
  23. fmt.Println(&quot;tuple:&quot;, tuple)
  24. }
  25. }

Output:

<pre>
tuple: [1 4 7]
tuple: [2 5 8]
tuple: [3 6 9]
</pre>

答案3

得分: 3

Go 1.18

通过支持类型参数,您可以编写一个可以将任意两个切片进行压缩的 zip 函数。

您可以声明一个可以容纳任意两种类型的元组结构,如下所示:

  1. type Pair[T, U any] struct {
  2. First T
  3. Second U
  4. }

然后是 zip 函数。它可以很简单,像这样:

  1. func Zip[T, U any](ts []T, us []U) []Pair[T,U] {
  2. if len(ts) != len(us) {
  3. panic("slices have different length")
  4. }
  5. pairs := make([]Pair[T,U], len(ts))
  6. for i := 0; i < len(ts); i++ {
  7. pairs[i] = Pair[T,U]{ts[i], us[i]}
  8. }
  9. return pairs
  10. }

示例用法:

  1. func main() {
  2. ts := []uint64{100, 200, 300}
  3. us := []string{"aa", "bb", "cc"}
  4. p := Zip(ts, us)
  5. fmt.Println(p)
  6. // 输出 [{100 aa} {200 bb} {300 cc}]
  7. }

您还可以修改上面的函数以压缩不同长度的切片,通过将较短切片的 Pair 字段保留为其零值:

  1. func ZipDiff[T, U any](ts []T, us []U) []Pair[T, U] {
  2. // 确定最小和最大长度
  3. lmin, lmax := minmax(len(ts), len(us))
  4. pairs := make([]Pair[T, U], lmax)
  5. // 构建到最小长度的元组
  6. for i := 0; i < lmin; i++ {
  7. pairs[i] = Pair[T, U]{ts[i], us[i]}
  8. }
  9. if lmin == lmax {
  10. return pairs
  11. }
  12. // 在 [lmin,lmax) 范围内构建一个零值的元组
  13. for i := lmin; i < lmax; i++ {
  14. p := Pair[T, U]{}
  15. if len(ts) == lmax {
  16. p.First = ts[i]
  17. } else {
  18. p.Second = us[i]
  19. }
  20. pairs[i] = p
  21. }
  22. return pairs
  23. }

示例:

  1. func main() {
  2. ts := []uint64{100}
  3. us := []string{"aa", "bb", "cc", "dd", "ee"}
  4. p := ZipDiff(ts, us)
  5. fmt.Println(p)
  6. // 输出 [{100 aa} {0 bb} {0 cc} {0 dd} {0 ee}]
  7. q := ZipDiff(us, ts)
  8. fmt.Println(q)
  9. // 输出 [{aa 100} {bb 0} {cc 0} {dd 0} {ee 0}]
  10. }

代码和 minmax 辅助函数可在 playground 中找到:https://go.dev/play/p/jpChqsl_GNl

英文:

Go 1.18

With the support for type parameters, you can write a zip function that zips any two slices.

You can declare a tuple struct that can hold any two types, like this:

  1. type Pair[T, U any] struct {
  2. First T
  3. Second U
  4. }

And the zip function. It can be as simple as:

  1. func Zip[T, U any](ts []T, us []U) []Pair[T,U] {
  2. if len(ts) != len(us) {
  3. panic(&quot;slices have different length&quot;)
  4. }
  5. pairs := make([]Pair[T,U], len(ts))
  6. for i := 0; i &lt; len(ts); i++ {
  7. pairs[i] = Pair[T,U]{ts[i], us[i]}
  8. }
  9. return pairs
  10. }

Example usage:

  1. func main() {
  2. ts := []uint64{100, 200, 300}
  3. us := []string{&quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;}
  4. p := Zip(ts, us)
  5. fmt.Println(p)
  6. // prints [{100 aa} {200 bb} {300 cc}]
  7. }

You can also modify the function above to zip slices of different lengths, by leaving the Pair field to its zero value for the shorter slice:

  1. func ZipDiff[T, U any](ts []T, us []U) []Pair[T, U] {
  2. // identify the minimum and maximum lengths
  3. lmin, lmax := minmax(len(ts), len(us))
  4. pairs := make([]Pair[T, U], lmax)
  5. // build tuples up to the minimum length
  6. for i := 0; i &lt; lmin; i++ {
  7. pairs[i] = Pair[T, U]{ts[i], us[i]}
  8. }
  9. if lmin == lmax {
  10. return pairs
  11. }
  12. // build tuples with one zero value for [lmin,lmax) range
  13. for i := lmin; i &lt; lmax; i++ {
  14. p := Pair[T, U]{}
  15. if len(ts) == lmax {
  16. p.First = ts[i]
  17. } else {
  18. p.Second = us[i]
  19. }
  20. pairs[i] = p
  21. }
  22. return pairs
  23. }

Example:

  1. func main() {
  2. ts := []uint64{100}
  3. us := []string{&quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;, &quot;dd&quot;, &quot;ee&quot;}
  4. p := ZipDiff(ts, us)
  5. fmt.Println(p)
  6. // prints [{100 aa} {0 bb} {0 cc} {0 dd} {0 ee}]
  7. q := ZipDiff(us, ts)
  8. fmt.Println(q)
  9. // prints [{aa 100} {bb 0} {cc 0} {dd 0} {ee 0}]
  10. }

Code and minmax helper func available in the playground: https://go.dev/play/p/jpChqsl_GNl

答案4

得分: -1

如果您需要zip函数的结果是一个map,可以使用comparable约束来实现。

  1. func zip[K comparable, V any](a []K, b []V) map[K]V {
  2. c := make(map[K]V)
  3. for i := 0; i < len(a); i++ {
  4. c[a[i]] = b[i]
  5. }
  6. return c
  7. }

这段代码使用了comparable约束来确保K类型是可比较的,从而可以作为map的键类型。函数将两个切片ab进行zip操作,将它们对应位置的元素组合成一个map,并返回该map

英文:

If you need the result of the zip function to be a map, this can be done with the comparable constraint

  1. func zip[K comparable, V any](a []K, b []V) map[K]V {
  2. c := make(map[K]V)
  3. for i := 0; i &lt; len(a); i++ {
  4. c[a[i]] = b[i]
  5. }
  6. return c
  7. }

huangapple
  • 本文由 发表于 2014年11月16日 20:44:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/26957040.html
匿名

发表评论

匿名网友

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

确定