将切片转换为元组的便捷函数?

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

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 &lt; 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 应该是 &amp;T 类型,最后一个参数必须是一个 'rest' &amp;[]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 &amp;T, except the last one, which must be a&#160;&#39;rest&#39; &amp;[]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 &lt; 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 &lt; 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, &amp;a, &amp;b, &amp;c, &amp;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 (
  &quot;fmt&quot;
)

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&#39;t work
  //g,h,i:= list[7:9]
}

huangapple
  • 本文由 发表于 2013年10月1日 04:38:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/19103386.html
匿名

发表评论

匿名网友

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

确定