英文:
Convenience function to convert slice to tuple?
问题
在Python中,可以像这样编写代码,从列表中分配多个值:
(a, b, c, d) = [1,2,3,4]
在Go语言中,是否有类似的切片库函数?也就是说,我可以这样做:
package main
func get3(s []interface{}) (
a interface{},
b interface{},
c interface{},
rest []interface{}) {
return s[0],s[1],s[2],s[4:]
}
func main() {
s := make([]interface{},5);
for i :=0 ; i < 5; i++ { s[i] = i}
a,b,c,_ := get3(s)
print(a.(int))
print(b.(int))
print(c.(int))
}
是否有一种标准的Go语言方式来实现这个?是否有一种避免使用interface{}
的方法?
英文:
In Python, one can write code like this, to assign multiple values from a list:
(a, b, c, d) = [1,2,3,4]
Is there a similar set of Go library function for slices? That is, I can do this:
http://play.golang.org/p/DY1Bi5omm1
package main
func get3(s []interface{}) (
a interface{},
b interface{},
c interface{},
rest []interface{}) {
return s[0],s[1],s[2],s[4:]
}
func main() {
s := make([]interface{},5);
for i :=0 ; i < 5; i++ { s[i] = i}
a,b,c,_ := get3(s)
print(a.(int))
print(b.(int))
print(c.(int))
}
Is there a standard gophery way to do this?
And is there a way around the interface{} ugliness?
答案1
得分: 2
不是这样的;你需要动态类型或参数化多态性,这在Go语言中是不可用的。我能想到的最接近的方法是使用反射(reflect)来操作,像这样:http://play.golang.org/p/-K4jh2nZjq
// src 应该是 []T 类型。
// dst 应该是 &T 类型,最后一个参数必须是一个 'rest' &[]T 类型(或者为 nil 表示丢弃)。
// dst 变量的数量不能超过 src 中元素的数量。
func extract(src interface{}, dst ...interface{}) {
srcV := reflect.ValueOf(src)
// 遍历 dst 变量直到用完为止。
i := 0
for i = 0; i < len(dst)-1; i++ {
reflect.Indirect(reflect.ValueOf(dst[i])).Set(srcV.Index(i))
}
// 处理剩余的部分。
restDst := dst[i]
if restDst == nil {
return
}
restV := reflect.ValueOf(restDst)
indirectRest := reflect.Indirect(restV)
l := srcV.Len() - i
indirectRest.Set(reflect.MakeSlice(restV.Type().Elem(), 0, l))
for ; i < srcV.Len(); i++ {
itemV := srcV.Index(i)
indirectRest.Set(reflect.Append(indirectRest, itemV))
}
return
}
然后你可以这样调用:
sl := []int{1, 2, 3, 4, 5, 6} // int 或其他类型
var a, b, c int
var rest []int
extract(sl, &a, &b, &c, &rest)
这样就可以在函数内部隐藏这些丑陋的操作。
但请注意,所有这些操作都是在运行时进行的,所以不安全、不高效,而且绝对不是Go语言的惯用方式。
英文:
Not like that; you would need dynamic typing or parametric polymorphism, which are not available in Go. The closest I can think about is by fiddling with reflect, like this: http://play.golang.org/p/-K4jh2nZjq
// src is supposed to be []T.
// dst are supposed to be &T, except the last one, which must be a 'rest' &[]T (or nil for discarding).
// There must not be more dst vars than elements in src.
func extract(src interface{}, dst ...interface{}) {
srcV := reflect.ValueOf(src)
// Iterate over dst vars until we run out of them.
i := 0
for i = 0; i < len(dst)-1; i++ {
reflect.Indirect(reflect.ValueOf(dst[i])).Set(srcV.Index(i))
}
// Now, the rest.
restDst := dst[i]
if restDst == nil {
return
}
restV := reflect.ValueOf(restDst)
indirectRest := reflect.Indirect(restV)
l := srcV.Len() - i
indirectRest.Set(reflect.MakeSlice(restV.Type().Elem(), 0, l))
for ; i < srcV.Len(); i++ {
itemV := srcV.Index(i)
indirectRest.Set(reflect.Append(indirectRest, itemV))
}
return
}
Which then you call like:
sl := []int{1, 2, 3, 4, 5, 6} // int or any other type
var a, b, c int
var rest []int
extract(sl, &a, &b, &c, &rest)
So the ugliness doesn't get out the function.
But note that all that happens at runtime, so it's not safe nor efficient and definitely is not idiomatic Go.
答案2
得分: 2
我不认为你可以以一种习惯的/清晰的方式做到这一点。你可以进行多个赋值,但是你必须直接或通过闭包传递单个值:
package main
import (
"fmt"
)
func valuesFromList(list []int, startFrom int) func() int {
i := startFrom
return func() int {
ret := list[i]
i++
return ret
}
}
func main() {
list := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
yield := valuesFromList(list, 5)
// 这样可以工作
a, b, c := yield(), yield(), yield()
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
// 这样也可以工作
d, e, f := list[0], list[1], list[2]
fmt.Println(d)
fmt.Println(e)
fmt.Println(f)
// 这样不会工作
// g, h, i := list[7:9]
}
英文:
I don't think you can, not in an idiomatic/clean way at least. You CAN do multiple assignments, but you will have to pass individual values either directly or with a closure:
package main
import (
"fmt"
)
func valuesFromList(list[]int,startFrom int) func() int {
i:=startFrom
return func() int {
ret := list[i]
i++
return ret
}
}
func main () {
list := []int{0,1,2,3,4,5,6,7,8,9}
yield := valuesFromList(list,5)
//This works
a,b,c := yield(),yield(),yield()
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
//This also works
d,e,f := list[0],list[1],list[2]
fmt.Println(d)
fmt.Println(e)
fmt.Println(f)
//This won't work
//g,h,i:= list[7:9]
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论