英文:
How to combine slices into a slice of tuples in Go (implementing python `zip` function)?
问题
有时候,在Python中可以使用zip
内置函数将两个列表合并成一个元组。那么在Go语言中如何实现类似的功能呢?
例如:
package main
import "fmt"
func main() {
list1 := []int{1, 2}
list2 := []int{3, 4}
result := make([][2]int, len(list1))
for i := range list1 {
result[i] = [2]int{list1[i], list2[i]}
}
fmt.Println(result)
}
这段代码将输出:
[[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:
>>> zip ([1,2],[3,4])
[(1,3), (2,4)]
答案1
得分: 19
你可以像这样做,给元组类型起一个名字:
package main
import "fmt"
type intTuple struct {
a, b int
}
func zip(a, b []int) ([]intTuple, error) {
if len(a) != len(b) {
return nil, fmt.Errorf("zip: arguments must be of same length")
}
r := make([]intTuple, len(a), len(a))
for i, e := range a {
r[i] = intTuple{e, b[i]}
}
return r, nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
fmt.Println(zip(a, b))
}
或者可以使用无名类型作为元组,像这样:
package main
import "fmt"
func zip(a, b []int) ([][2]int, error) {
if len(a) != len(b) {
return nil, fmt.Errorf("zip: arguments must be of same length")
}
r := make([][2]int, len(a), len(a))
for i, e := range a {
r[i] = [2]int{e, b[i]}
}
return r, nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
fmt.Println(zip(a, b))
}
最后,这是一种软通用的方法:
package main
import (
"fmt"
"reflect"
)
func zip(a, b, c interface{}) error {
ta, tb, tc := reflect.TypeOf(a), reflect.TypeOf(b), reflect.TypeOf(c)
if ta.Kind() != reflect.Slice || tb.Kind() != reflect.Slice || ta != tb {
return fmt.Errorf("zip: first two arguments must be slices of the same type")
}
if tc.Kind() != reflect.Ptr {
return fmt.Errorf("zip: third argument must be pointer to slice")
}
for tc.Kind() == reflect.Ptr {
tc = tc.Elem()
}
if tc.Kind() != reflect.Slice {
return fmt.Errorf("zip: third argument must be pointer to slice")
}
eta, _, etc := ta.Elem(), tb.Elem(), tc.Elem()
if etc.Kind() != reflect.Array || etc.Len() != 2 {
return fmt.Errorf("zip: third argument's elements must be an array of length 2")
}
if etc.Elem() != eta {
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")
}
va, vb, vc := reflect.ValueOf(a), reflect.ValueOf(b), reflect.ValueOf(c)
for vc.Kind() == reflect.Ptr {
vc = vc.Elem()
}
if va.Len() != vb.Len() {
return fmt.Errorf("zip: first two arguments must have same length")
}
for i := 0; i < va.Len(); i++ {
ea, eb := va.Index(i), vb.Index(i)
tt := reflect.New(etc).Elem()
tt.Index(0).Set(ea)
tt.Index(1).Set(eb)
vc.Set(reflect.Append(vc, tt))
}
return nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
c := [][2]int{}
e := zip(a, b, &c)
if e != nil {
fmt.Println(e)
return
}
fmt.Println(c)
}
英文:
You could do something like this, where you give the tuple type a name:
package main
import "fmt"
type intTuple struct {
a, b int
}
func zip(a, b []int) ([]intTuple, error) {
if len(a) != len(b) {
return nil, fmt.Errorf("zip: arguments must be of same length")
}
r := make([]intTuple, len(a), len(a))
for i, e := range a {
r[i] = intTuple{e, b[i]}
}
return r, nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
fmt.Println(zip(a, b))
}
Or alternatively use an unnamed type for the tuple, like this:
package main
import "fmt"
func zip(a, b []int) ([][3]int, error) {
if len(a) != len(b) {
return nil, fmt.Errorf("zip: arguments must be of same length")
}
r := make([][4]int, len(a), len(a))
for i, e := range a {
r[i] = [2]int{e, b[i]}
}
return r, nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
fmt.Println(zip(a, b))
}
And finally here's a soft-generic way of doing it:
package main
import (
"fmt"
"reflect"
)
func zip(a, b, c interface{}) error {
ta, tb, tc := reflect.TypeOf(a), reflect.TypeOf(b), reflect.TypeOf(c)
if ta.Kind() != reflect.Slice || tb.Kind() != reflect.Slice || ta != tb {
return fmt.Errorf("zip: first two arguments must be slices of the same type")
}
if tc.Kind() != reflect.Ptr {
return fmt.Errorf("zip: third argument must be pointer to slice")
}
for tc.Kind() == reflect.Ptr {
tc = tc.Elem()
}
if tc.Kind() != reflect.Slice {
return fmt.Errorf("zip: third argument must be pointer to slice")
}
eta, _, etc := ta.Elem(), tb.Elem(), tc.Elem()
if etc.Kind() != reflect.Array || etc.Len() != 2 {
return fmt.Errorf("zip: third argument's elements must be an array of length 2")
}
if etc.Elem() != eta {
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")
}
va, vb, vc := reflect.ValueOf(a), reflect.ValueOf(b), reflect.ValueOf(c)
for vc.Kind() == reflect.Ptr {
vc = vc.Elem()
}
if va.Len() != vb.Len() {
return fmt.Errorf("zip: first two arguments must have same length")
}
for i := 0; i < va.Len(); i++ {
ea, eb := va.Index(i), vb.Index(i)
tt := reflect.New(etc).Elem()
tt.Index(0).Set(ea)
tt.Index(1).Set(eb)
vc.Set(reflect.Append(vc, tt))
}
return nil
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
b := []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}
c := [][2]int{}
e := zip(a, b, &c)
if e != nil {
fmt.Println(e)
return
}
fmt.Println(c)
}
答案2
得分: 14
要将一些[]int
切片列表进行zip
操作,
package main
import "fmt"
func zip(lists ...[]int) func() []int {
zip := make([]int, len(lists))
i := 0
return func() []int {
for j := range lists {
if i >= len(lists[j]) {
return nil
}
zip[j] = lists[j][i]
}
i++
return zip
}
}
func main() {
a := []int{1, 2, 3}
b := []int{4, 5, 6}
c := []int{7, 8, 9, 0}
iter := zip(a, b, c)
for tuple := iter(); tuple != nil; tuple = iter() {
fmt.Println("tuple:", tuple)
}
}
输出结果:
tuple: [1 4 7]
tuple: [2 5 8]
tuple: [3 6 9]
英文:
To zip
some number of slice []int
lists,
package main
import "fmt"
func zip(lists ...[]int) func() []int {
zip := make([]int, len(lists))
i := 0
return func() []int {
for j := range lists {
if i >= len(lists[j]) {
return nil
}
zip[j] = lists[j][i]
}
i++
return zip
}
}
func main() {
a := []int{1, 2, 3}
b := []int{4, 5, 6}
c := []int{7, 8, 9, 0}
iter := zip(a, b, c)
for tuple := iter(); tuple != nil; tuple = iter() {
fmt.Println("tuple:", tuple)
}
}
Output:
<pre>
tuple: [1 4 7]
tuple: [2 5 8]
tuple: [3 6 9]
</pre>
答案3
得分: 3
Go 1.18
通过支持类型参数,您可以编写一个可以将任意两个切片进行压缩的 zip 函数。
您可以声明一个可以容纳任意两种类型的元组结构,如下所示:
type Pair[T, U any] struct {
First T
Second U
}
然后是 zip 函数。它可以很简单,像这样:
func Zip[T, U any](ts []T, us []U) []Pair[T,U] {
if len(ts) != len(us) {
panic("slices have different length")
}
pairs := make([]Pair[T,U], len(ts))
for i := 0; i < len(ts); i++ {
pairs[i] = Pair[T,U]{ts[i], us[i]}
}
return pairs
}
示例用法:
func main() {
ts := []uint64{100, 200, 300}
us := []string{"aa", "bb", "cc"}
p := Zip(ts, us)
fmt.Println(p)
// 输出 [{100 aa} {200 bb} {300 cc}]
}
您还可以修改上面的函数以压缩不同长度的切片,通过将较短切片的 Pair
字段保留为其零值:
func ZipDiff[T, U any](ts []T, us []U) []Pair[T, U] {
// 确定最小和最大长度
lmin, lmax := minmax(len(ts), len(us))
pairs := make([]Pair[T, U], lmax)
// 构建到最小长度的元组
for i := 0; i < lmin; i++ {
pairs[i] = Pair[T, U]{ts[i], us[i]}
}
if lmin == lmax {
return pairs
}
// 在 [lmin,lmax) 范围内构建一个零值的元组
for i := lmin; i < lmax; i++ {
p := Pair[T, U]{}
if len(ts) == lmax {
p.First = ts[i]
} else {
p.Second = us[i]
}
pairs[i] = p
}
return pairs
}
示例:
func main() {
ts := []uint64{100}
us := []string{"aa", "bb", "cc", "dd", "ee"}
p := ZipDiff(ts, us)
fmt.Println(p)
// 输出 [{100 aa} {0 bb} {0 cc} {0 dd} {0 ee}]
q := ZipDiff(us, ts)
fmt.Println(q)
// 输出 [{aa 100} {bb 0} {cc 0} {dd 0} {ee 0}]
}
代码和 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:
type Pair[T, U any] struct {
First T
Second U
}
And the zip function. It can be as simple as:
func Zip[T, U any](ts []T, us []U) []Pair[T,U] {
if len(ts) != len(us) {
panic("slices have different length")
}
pairs := make([]Pair[T,U], len(ts))
for i := 0; i < len(ts); i++ {
pairs[i] = Pair[T,U]{ts[i], us[i]}
}
return pairs
}
Example usage:
func main() {
ts := []uint64{100, 200, 300}
us := []string{"aa", "bb", "cc"}
p := Zip(ts, us)
fmt.Println(p)
// prints [{100 aa} {200 bb} {300 cc}]
}
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:
func ZipDiff[T, U any](ts []T, us []U) []Pair[T, U] {
// identify the minimum and maximum lengths
lmin, lmax := minmax(len(ts), len(us))
pairs := make([]Pair[T, U], lmax)
// build tuples up to the minimum length
for i := 0; i < lmin; i++ {
pairs[i] = Pair[T, U]{ts[i], us[i]}
}
if lmin == lmax {
return pairs
}
// build tuples with one zero value for [lmin,lmax) range
for i := lmin; i < lmax; i++ {
p := Pair[T, U]{}
if len(ts) == lmax {
p.First = ts[i]
} else {
p.Second = us[i]
}
pairs[i] = p
}
return pairs
}
Example:
func main() {
ts := []uint64{100}
us := []string{"aa", "bb", "cc", "dd", "ee"}
p := ZipDiff(ts, us)
fmt.Println(p)
// prints [{100 aa} {0 bb} {0 cc} {0 dd} {0 ee}]
q := ZipDiff(us, ts)
fmt.Println(q)
// prints [{aa 100} {bb 0} {cc 0} {dd 0} {ee 0}]
}
Code and minmax
helper func available in the playground: https://go.dev/play/p/jpChqsl_GNl
答案4
得分: -1
如果您需要zip
函数的结果是一个map
,可以使用comparable
约束来实现。
func zip[K comparable, V any](a []K, b []V) map[K]V {
c := make(map[K]V)
for i := 0; i < len(a); i++ {
c[a[i]] = b[i]
}
return c
}
这段代码使用了comparable
约束来确保K
类型是可比较的,从而可以作为map
的键类型。函数将两个切片a
和b
进行zip
操作,将它们对应位置的元素组合成一个map
,并返回该map
。
英文:
If you need the result of the zip
function to be a map
, this can be done with the comparable
constraint
func zip[K comparable, V any](a []K, b []V) map[K]V {
c := make(map[K]V)
for i := 0; i < len(a); i++ {
c[a[i]] = b[i]
}
return c
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论