在赋值时解包切片吗?

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

Unpack slices on assignment?

问题

在Go语言中,有没有一种像Python中那样从数组中进行多重赋值的优雅方式?以下是我尝试做的一个Python示例(将一个字符串拆分并将结果数组分配给两个变量)。

  1. python:
  2. >>> a, b = "foo;bar".split(";")

我目前的解决方案是:

  1. x := strings.Split("foo;bar", ";")
  2. a, b := x[0], x[1]

我可以看到在某些情况下,这种方法可能会变得混乱。我目前面临的实际例子是解析书签文件并将其分配给一个映射:

  1. bookmark := make(map[string]string)
  2. x := strings.Split("foo\thttps://bar", "\t")
  3. name, link := x[0], x[1]
  4. bookmark[name] = link

现在我有一个无用的变量x。我想做的是:

  1. bookmark := make(map[string]string)
  2. name, link := strings.Split("foo\thttps://bar", "\t")
  3. bookmark[name] = link

但这是无效的。

英文:

Is there an elegant way in Go to do multiple assignments from arrays like in Python? Here is a Python example of what I'm trying to do (split a string and then assign the resulting array into two variables).

  1. python:
  2. >>> a, b = "foo;bar".split(";")

My current solution is:

  1. x := strings.Split("foo;bar", ";")
  2. a, b := x[0], x[1]

I'm can see this getting messy in some constructs. The practical example I'm currently facing is a bookmark file parsing and assigning to a map:

  1. bookmark := make(map[string]string)
  2. x := strings.Split("foo\thttps://bar", "\t")
  3. name, link := x[0], x[1]
  4. bookmark[name] = link

Now I have a useless variable x sitting around. I'd like to do something like:

  1. bookmark := make(map[string]string)
  2. name, line := strings.Split("foo\thttps://bar", "\t")
  3. bookmark[name] = link

but that's invalid.

答案1

得分: 69

如Sergio Tulentsev所提到的,Python中的通用打包/解包操作在这里是不支持的。我认为解决这个问题的方法是定义一个自己的小型临时函数,使用多个返回值:

  1. func splitLink(s, sep string) (string, string) {
  2. x := strings.Split(s, sep)
  3. return x[0], x[1]
  4. }

然后你可以这样写:

  1. name, link := splitLink("foo\thttps://bar", "\t")

但是这显然只适用于至少拆分两个子字符串的情况,并且如果拆分的子字符串超过两个,它会默默地忽略掉。如果你经常使用这种方式,它可能会使你的代码更易读。

--编辑--

另一种解包数组的方法是使用可变指针参数:

  1. func unpack(s []string, vars... *string) {
  2. for i, str := range s {
  3. *vars[i] = str
  4. }
  5. }

这样你可以这样写:

  1. var name, link string
  2. unpack(strings.Split("foo\thttps://bar", "\t"), &name, &link)
  3. bookmarks[name] = link

这种方法适用于任何数组大小,但可读性较差,并且你必须显式声明变量。

英文:

As Sergio Tulentsev mentioned, general packing/unpacking as is done in Python is not supported. I think the way to go there is to define your own small ad-hoc function using multiple return values:

  1. func splitLink(s, sep string) (string, string) {
  2. x := strings.Split(s, sep)
  3. return x[0], x[1]
  4. }

And you can then write:

  1. name, link := splitLink("foo\thttps://bar", "\t")

But this will obviously work only when at least two substrings are being split, and silently ignore if more than two were. If this is something you use a lot, it might make your code more readable though.

--EDIT--

Another way to unpack an array is via variadic pointer arguments:

  1. func unpack(s []string, vars... *string) {
  2. for i, str := range s {
  3. *vars[i] = str
  4. }
  5. }

Which let you write:

  1. var name, link string
  2. unpack(strings.Split("foo\thttps://bar", "\t"), &name, &link)
  3. bookmarks[name] = link

This will work for any array size, but it is arguably less readable, and you have to declare your variables explicitly.

答案2

得分: 16

你也可以使用匿名函数:

  1. a, b := func() (string, string) {
  2. x := strings.Split("foo;bar", ";")
  3. return x[0], x[1]
  4. }()
  5. 注意不要忘记在闭合括号 `}` 的末尾加上 `()`否则会出现错误
  6. ```assignment mismatch: 2 variable but 1 values```
  7. 这是因为没有加上 `()`返回的是一个函数1个值),而不是预期的字符串2个值)。
  8. <details>
  9. <summary>英文:</summary>
  10. You could also use anonymous functions:
  11. a, b := func() (string, string) {
  12. x := strings.Split(&quot;foo;bar&quot;, &quot;;&quot;)
  13. return x[0], x[1]
  14. }()
  15. Note: don&#39;t forget the `()` on the end of the closing bracket `}` otherwise you will get the error:
  16. ```assignment mismatch: 2 variable but 1 values```
  17. This is because without the `()` a function (1 value) is returned not the expected strings (2 values).
  18. </details>
  19. # 答案3
  20. **得分**: 9
  21. 如果你的函数的目的是仅通过分隔符的第一个出现来拆分字符串你可以自己编写一个函数
  22. ```go
  23. package main
  24. import (
  25. "fmt"
  26. "strings"
  27. )
  28. func Split(s, sep string) (string, string) {
  29. // 空字符串应该返回空
  30. if len(s) == 0 {
  31. return s, s
  32. }
  33. slice := strings.SplitN(s, sep, 2)
  34. // 如果没有分隔符
  35. if len(slice) == 1 {
  36. return slice[0], ""
  37. }
  38. return slice[0], slice[1]
  39. }
  40. func main() {
  41. a, b := Split("foo;bar;foo", ";")
  42. fmt.Println(a, b)
  43. }

输出:

foo bar;foo

Playground

英文:

If your function is meant to split a string only by the first occurrence of the separator, you can always make your own function:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;strings&quot;
  5. )
  6. func Split(s, sep string) (string, string) {
  7. // Empty string should just return empty
  8. if len(s) == 0 {
  9. return s, s
  10. }
  11. slice := strings.SplitN(s, sep, 2)
  12. // Incase no separator was present
  13. if len(slice) == 1 {
  14. return slice[0], &quot;&quot;
  15. }
  16. return slice[0], slice[1]
  17. }
  18. func main() {
  19. a, b := Split(&quot;foo;bar;foo&quot;, &quot;;&quot;)
  20. fmt.Println(a, b)
  21. }

Output:

>foo bar;foo

Playground

huangapple
  • 本文由 发表于 2013年11月7日 17:27:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/19832189.html
匿名

发表评论

匿名网友

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

确定