我在this question中偶然发现了递归类型定义:
type N []N
据我所知,它定义了一个名为 N 的类型,它是 N 类型数组的一部分,这意味着该类型定义是递归的。
将此代码视为有效的理由是什么?
有哪些用例的例子?
我试着用这个做实验:
package main
import "fmt"
type N []N
func main() {
var n N
fmt.Printf("Naked: %T %v\n", n, n)
n = make(N, 1)
fmt.Printf("First: %T %v\n", n, n)
n[0] = make(N, 1)
fmt.Printf("Second: %T %v %T %v\n", n, n, n[0], n[0])
n[0][0] = make(N, 1)
fmt.Printf("Third: %T %v %T %v %T %v\n", n, n, n[0], n[0], n[0][0], n[0][0])
n[0] = n
fmt.Println("Drum roll")
fmt.Printf("Blatant: %T %v %T %v\n", n, n, n[0], n[0])
fmt.Println("Reached the main end")
}
运行这段代码没有给我任何答案,只有更多问题:
Printf
)port Naked: main.N []
First: main.N [[]]
Second: main.N [[[]]] main.N [[]]
Third: main.N [[[[]]]] main.N [[[]]] main.N [[]]
Drum roll
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020160358 stack=[0xc020160000, 0xc040160000]
fatal error: stack overflow
runtime stack:
runtime.throw({0x49ae35?, 0x518860?})
/usr/lib/go/src/runtime/panic.go:1047 +0x5d fp=0xc0004b1e10 sp=0xc0004b1de0 pc=0x43177d
runtime.newstack()
/usr/lib/go/src/runtime/stack.go:1103 +0x5cc fp=0xc0004b1fc8 sp=0xc0004b1e10 pc=0x448e2c
runtime.morestack()
/usr/lib/go/src/runtime/asm_amd64.s:570 +0x8b fp=0xc0004b1fd0 sp=0xc0004b1fc8 pc=0x45ae4b
goroutine 1 [running]:
runtime.assertE2I2(0x48dae0?, {0x48b200?, 0xc001d16918?})
/usr/lib/go/src/runtime/iface.go:456 +0x78 fp=0xc020160368 sp=0xc020160360 pc=0x409c78
fmt.(*pp).handleMethods(0xc000118270, 0x116018?)
/usr/lib/go/src/fmt/print.go:621 +0xdd fp=0xc0201605b8 sp=0xc020160368 pc=0x47dd1d
fmt.(*pp).printValue(0xc000118270, {0x48b200?, 0xc000116018?, 0x0?}, 0x76, 0x10841b)
/usr/lib/go/src/fmt/print.go:754 +0xe5 fp=0xc0201607a8 sp=0xc0201605b8 pc=0x47ed45
fmt.(*pp).printValue(0xc000118270, {0x48b200?, 0xc000116018?, 0x0?}, 0x76, 0x10841a)
/usr/lib/go/src/fmt/print.go:896 +0x16b2 fp=0xc020160998 sp=0xc0201607a8 pc=0x480312
fmt.(*pp).printValue(0xc000118270, {0x48b200?, 0xc000116018?, 0x0?}, 0x76, 0x108419)
/usr/lib/go/src/fmt/print.go:896 +0x16b2 fp=0xc020160b88 sp=0xc020160998 pc=0x480312"fmt"
在尝试解决这个问题时,我通读了this question about recursive function definitions。
type Func func()(int int Func)
我确实理解,但它似乎与 N
定义不同,因为它与可以调用 self 的该类型的实际函数的定义一起使用。
看来,当我写这个问题时,我开始理解上面代码的行为:
n
只需要当场分配内存给指向数组的指针,段的长度,和它的容量n[0][0]
也是一样,结果slice of slice of slice with nullptr
为最内层slicePrintf
中的反射工作正常,并且 n
的值预计会从“空切片”变为“一个空切片的切片”到......n[0] = n
布设地雷的时候:它使n[0]
的ptr指向n
,在切片的嵌套中创建一个循环Printf
找出 n
的类型使其递归追逐该循环并溢出堆栈。但这一切都没有回答问题,这能用来做什么?还是应该避免的语言中的意外怪癖?
我认为你被周期绊倒了。这些可能是可能的,但总的来说我认为指针是一个更好的主意。在对递归对象进行推理时,我发现首先了解终止情况会很有帮助。在你的例子中,那将是
nil
.
所以一旦你有了它,你就可以使用新类型轻松地创建一个对象。这是一个基于您的代码的工作示例,减去 周期:
package main
import "fmt"
type N []N
func main() {
n := N{
N{
N{nil},
},
}
fmt.Println(n) // [[[[]]]]
}