三个点(…)是否有多重含义?

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

Do three dots contain multiple meanings?

问题

据我所了解,下面代码片段中的"..."表示数组的长度。

var days := [...]string { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } 

另一方面,在下面的代码片段中,"..."表示将切片y解包为int类型的参数,我猜测是这样的。对于这一点我不太确定。

x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)

现在,这两种含义的差异使我很难理解"..."的含义。

英文:

As I recognize, "..." means the length of the array in the below snippet.

var days := [...]string { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } 

On the other hand, "..." means unpacking the slice y to arguments of int in the below snippet, as I guess. I'm not really sure about this.

x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)

Now, the difference in the two meanings makes it hard for me to understand what "..." is.

答案1

得分: 32

你已经注意到了Go语言中...的两种用法,实际上有三种情况:

  1. [...]int{1,2,3}:在编译时评估为[3]int{1,2,3},表示省略了数组的大小,让编译器自动推断。

  2. a := make([]int, 500)SomeVariadicFunc(a...):将a解包作为函数的参数。这是你漏掉的一种情况,对应可变参数的定义func SomeVariadicFunc(a ...int)

那么进一步的问题是,为什么...在所有这些情况下都能起作用?答案是,在英语(和其他语言)中,这被称为省略号(ellipsis)。根据维基百科的解释:

省略号(复数为ellipses;来自古希腊语:ἔλλειψις,élleipsis,意为“省略”或“不足”)是一系列点,通常表示有意省略了文本中的一个词、一个句子或一个整个部分,而不改变其原始含义。根据其上下文和在句子中的位置,省略号还可以表示一个未完成的思想、一个引导性陈述、一个轻微的停顿以及紧张或尴尬的沉默。

在数组的情况下,这符合“省略了文本中的一个词、一个句子或一个整个部分”的定义。你省略了数组的大小,让编译器为你计算。

在可变参数的情况下,它使用相同的含义,但用法不同。它还带有“未完成的思想”的暗示。我们经常使用“...”表示“等等”。比如,“我要买面包、鸡蛋、牛奶...”,在这种情况下,“...”表示“类似面包、鸡蛋和牛奶的其他东西”。在append等函数中的使用表示“这个列表的一个元素,以及其他所有元素”。这可能不是最直观的用法,但对于母语为英语的人来说是有意义的。也许一个更“语言纯粹”的构造方式可以是a[0]...或者a[0],a[1],a[2]...,但这会导致空切片的明显问题(而使用...语法是有效的),而且冗长。

总的来说,“...”用于表示“许多东西”,这样两种用法都是有意义的。许多数组元素,许多切片元素(尽管一个是创建,另一个是调用)。

我想隐藏的问题是“这是好的语言设计吗?”一方面,一旦你了解了语法,对于大多数以英语为母语的人来说,它是完全合理的,所以从这个意义上说,它是成功的。另一方面,不过载这种方式的符号也有其价值。我可能会选择一个不同的符号来表示数组解包,但我不能因为语言设计者可能觉得这个符号直观而责怪他们。尤其是因为数组版本并不经常使用。

正如前面提到的,对于编译器来说,这不是问题,因为这些情况永远不会重叠。[...]永远不会同时表示“解包”和其他含义,所以没有符号冲突。

(另外:Go语言中还有另一种用法,不是在语言本身中,而是在构建工具中。比如,输入go test ./...表示“测试这个包以及其子目录中的所有包”。但是通过我对其他用法的解释,这个用法应该很清楚。)

英文:

You've noted two cases of ... in Go. In fact, there are 3:

[...]int{1,2,3}

Evaluates at compile time to [3]int{1,2,3}

a := make([]int, 500)
SomeVariadicFunc(a...)

Unpacks a as the arguments to a function. This matches the one you missed, the variadic definition:

func SomeVariadicFunc(a ...int)

Now the further question (from the comments on the OP) -- why can ... work semantically in all these cases? The answer is that in English (and other languages), this is known as an ellipsis. From that article

> Ellipsis (plural ellipses; from the Ancient Greek: ἔλλειψις,
> élleipsis, "omission" or "falling short") is a series of dots that
> usually indicates an intentional omission of a word, sentence, or
> whole section from a text without altering its original meaning.1
> Depending on their context and placement in a sentence, ellipses can
> also indicate an unfinished thought, a leading statement, a slight
> pause, and a nervous or awkward silence.

In the array case, this matches the "omission of a word, sentence, or whole section" definition. You're omitting the size of the array and letting the compiler figure it out for you.

In the variadic cases, it uses the same meaning, but differently. It also has hints of "an unfinished thought". We often use "..." to mean "and so on." "I'm going to get bread, eggs, milk..." in this case "..." signifies "other things similar to breads, eggs, and milk". The use in, e.g., append means "an element of this list, and all the others." This is perhaps the less immediately intuitive usage, but to a native speaker, it makes sense. Perhaps a more "linguistically pure" construction would have been a[0]... or even a[0], a[1], a[2]... but that would cause obvious problems with empty slices (which do work with the ... syntax), not to mention being verbose.

In general, "..." is used to signify "many things", and in this way both uses of it make sense. Many array elements, many slice elements (albeit one is creation, and the other is calling).

I suppose the hidden question is "is this good language design?" On one hand, once you know the syntax, it makes perfect sense to most native speakers of English, so in that sense it's successful. On the other hand, there's value in not overloading symbols in this way. I probably would have chose a different symbol for array unpacking, but I can't fault them for using a symbol that was probably intuitive to the language designers. Especially since the array version isn't even used terribly often.

As mentioned, this is of no issue to the compiler, because the cases can never overlap. You can never have [...] also mean "unpack this", so there's no symbol conflict.


(Aside: There is another use of it in Go I omitted, because it's not in the language itself, but the build tool. Typing something like go test ./... means "test this package, and all packages in subdirectories of this one". But it should be pretty clear with my explanation of the other uses why it makes sense here.)

答案2

得分: 0

只是提供信息,myfunc(s...)并不意味着“解包”输入的s

相反,“绕过”可能是一个更合适的表达。

如果s是一个切片s := []string{"a", "b", "c"}

myfunc(s...)myfunc(s[0], s[1], s[2])不等价

这个简单的代码展示了这一点。


另外,请参阅官方的Go规范(稍作修改以提高清晰度):

> 给定函数
>
> func Greeting(prefix string, who ...string)
>
> 如果最后一个参数可以赋值给切片类型[]T,并且后面跟着...,它将作为...T参数的值传递,不会创建新的切片
>
> 给定切片s和调用
>
> s := []string{"James", "Jasmine"}
>
> Greeting("goodbye:", s...)
>
> 在Greeting函数内部,who将具有与s相同的值和相同的底层数组

如果它“解包”输入参数,应该创建一个具有不同数组的新切片(但事实并非如此)。

注意:这并不是真正的“绕过”,因为切片本身(而不是底层数组)被复制到函数中(Go中没有“引用”)。但是,函数内部的切片指向同一个原始的底层数组,所以“绕过”比“解包”更好地描述了这种情况。

英文:

Just FYI, myfunc(s...) does not mean "unpack" the input s.

Rather, "bypass" would be a more suitable expression.

If s is a slice s := []string{"a", "b", "c"},

myfunc(s...) is not equivalent to myfunc(s[0], s[1], s[2]).

This simple code shows it.


Also, see the official Go specification (slightly modified for clarity):

> Given the function
>
> func Greeting(prefix string, who ...string)
>
> If the final argument is assignable to a slice type []T and is
> followed by ..., it is passed unchanged as the value for a ...T
> parameter. In this case no new slice is created.
>
> Given the slice s and call
>
> s := []string{"James", "Jasmine"}
>
> Greeting("goodbye:", s...)
>
> within Greeting, who will have the same value as s with the same underlying
> array.

If it "unpacks" the input argument, a new slice with a different array should be created (which is not the case).

Note: It's not real "bypass" because the slice itself (not the underlying array) is copied into the function (there is no 'reference' in Go). But, that slice within the function points to the same original underlying array, so it would be a better description than "unpack".

huangapple
  • 本文由 发表于 2014年6月21日 17:48:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/24340379.html
匿名

发表评论

匿名网友

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

确定