切片如何包含自身? [英] How can a slice contain itself?

查看:27
本文介绍了切片如何包含自身?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Go 编程语言"来学习 Golang,并且我已经到达了切片部分.他们在数组和切片之间进行比较,因为两个数组可以与 == 进行比较,而两个切片则不能.正文如下:

I'm trying to learn Golang using "The Go Programming Language" and I've reached the section on slices. They make the comparison between arrays and slices in that two arrays can be compared with == where two slices can not. The text reads as the following:

"== operator for arrays of strings, it may be puzzling that slice
comparisons do not also work this way. There are two reasons why deep 
equivalence is problematic. First, unlike array elements, the elements
of a slice are indirect, making it possible for a slice to contain 
itself. Although there are ways to deal with such cases, none is 
simple, efficient, and most importantly, obvious."

由于元素是间接的,切片可能包含自身是什么意思?

What is meant by it's possible for a slice to contain itself due to the elements being indirect?

推荐答案

包含自身的切片

除了递归类型(例如 type Foo []Foo,请参阅 ANisus 的回答),除了演示之外,它一无是处,如果例如切片的元素类型是接口{}:

Slice containing itself

Besides a recursive type (such as type Foo []Foo, see ANisus's answer) which is good for nothing besides demonstration, a slice may contain itself if for example the element type of the slice is interface{}:

s := []interface{}{"one", nil}
s[1] = s

在这个例子中,切片 s 将有 2 个接口值,第一个包装"一个简单的字符串 one",另一个接口值包装切片值本身.创建接口值时,将包装该值的副本,在切片的情况下,这意味着切片头/描述符的副本,其中包含指向底层数组的指针,因此副本将具有相同的指针值指向相同的底层数组.(有关接口表示的更多详细信息,请参阅 反射定律:界面.)

In this example the slice s will have 2 interface values, the first "wrapping" a simple string "one", and another interface value wrapping the slice value itself. When an interface value is created, a copy of the value will be wrapped which in case of slices means a copy of the slice header/descriptor, which contains the pointer to the underlying array, so the copy will have the same pointer value pointing to the same underlying array. (For more details about the representation of interfaces, see The Laws of Reflection: The representation of an interface.)

如果您很快就可以打印出来:

If you were quickly on to print it:

fmt.Println(s)

你会得到一个致命的错误,比如:

You would get a fatal error, something like:

runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow

因为 fmt.Println() 尝试递归打印内容,并且由于第二个元素是一个切片,指向正在打印的切片的同一数组,所以它会陷入无限循环.

Because fmt.Println() tries to print the content recursively, and since the 2nd element is a slice pointing to the same array of the the slice being printed, it runs into an infinite loop.

另一种查看它是否真的是切片本身的方法:

Another way to see if it really is the slice itself:

s := []interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])

s2 := s[1].([]interface{})
fmt.Println(s2[0])

s3 := s2[1].([]interface{})
fmt.Println(s3[0])

输出(在 Go Playground 上试试):

Output (try it on the Go Playground):

one
one
one

无论我们走多深,第二个元素总是指向与 s 相同的数组的切片值,包裹在 interface{} 值中.

No matter how deep we go, the 2nd element will always be the slice value pointing to the same array as s, wrapped in an interface{} value.

间接性起着重要的作用,因为副本将包装在 interface{} 中,但副本将包含相同的指针.

The indirection plays the important role as a copy will be wrapped in the interface{} but the copy will contain the same pointer.

将类型更改为数组:

s := [2]interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])

s2 := s[1].([2]interface{})
fmt.Println(s2[0])

s3 := s2[1].([2]interface{})
fmt.Println(s3[0])

输出(在 Go Playground 上试试):

Output (try it on the Go Playground):

one
one
panic: interface conversion: interface is nil, not [2]interface {}

这是因为当数组被包装到 interface{} 时,一个副本将被包装 - 并且副本不是原始数组.所以 s 将有第二个值,一个 interface{} 包装一个数组,但这是一个不同的数组,它的第二个值没有设置,因此将是 nil(interface{} 类型的零值),因此尝试进入"这个数组会导致恐慌,因为它是 nil(类型断言 失败,因为没有使用特殊的逗号,好的"形式).

This is because when the array is wrapped into an interface{}, a copy will be wrapped - and a copy is not the original array. So s will have a second value, an interface{} wrapping an array, but that is a different array whose 2nd value is not set and therefore will be nil (the zero value of type interface{}), so attempting to "go into" this array will panic because it is nil (type assertion fails because not the special "comma, ok" form was used).

由于这个 s 数组不包含自身,一个简单的 fmt.Println() 将显示其全部内容:

Since this s array does not contain itself, a simple fmt.Println() will reveal its full content:

fmt.Println(s)

输出:

[one [one <nil>]]

进一步接口{}包装分析

如果将数组包裹在 interface{} 中并修改原始数组的内容,则包裹在 interface{} 中的值不受影响:

Further interface{} wrapping analysis

If you wrap an array in an interface{} and modify the content of the original array, the value wrapped in the interface{} is not affected:

arr := [2]int{1, 2}
var f interface{} = arr
arr[0] = 11

fmt.Println("Original array:    ", arr)
fmt.Println("Array in interface:", f)

输出:

Original array:     [11 2]
Array in interface: [1 2]

如果你对切片做同样的事情,被包裹的切片(因为指向同一个底层数组)也会受到影响:

If you do the same with a slice, the wrapped slice (since points to the same underlying array) is also affected:

s := []int{1, 2}
f = s
s[0] = 11

fmt.Println("Original slice:    ", s)
fmt.Println("Slice in interface:", f)

输出:

Original slice:     [11 2]
Slice in interface: [11 2]

Go Playground 上试试这些.

这篇关于切片如何包含自身?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆