在this问题中偶然发现递归类型定义:
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关于递归函数定义的问题。type Func func()(int int Func)
我确实理解,但它似乎与N
定义不同,因为它与可以调用self的该类型的实际函数的定义一起使用。
似乎当我写这个问题时,我开始理解上面代码的行为:
- 声明变量
n
时,必须在现场仅为pointer to the array, the length of the segment, and its capacity分配内存 - 初始化n[0]时,将创建一个元组数组(ptr、len、cap),其大小与存储的元素类型无关
- 初始化
n[0][0]
也是如此,结果是将nullptr
作为最内层片的数组指针,得到一片又一片的片 - 由于
Printf
中的此反射工作正常,因此n
的值预期会不断从“空切片”更改为“一个空切片的切片”,再更改为... - 这就是
n[0] = n
引爆地雷的原因:它使n[0]
的ptr指向n
,从而在片的嵌套中创建循环 - 指示
Printf
判断n
的类型会使它递归地跟踪该循环并溢出堆栈。
但所有这些都没有回答这个问题,这能用来做什么?或者这是语言中一个应该避免的无意的怪癖吗?
1条答案
按热度按时间zysjyyx41#
我认为你在循环上被绊倒了。那些可能是可能的,但是一般来说我认为指针是一个更好的主意。当推理递归对象时,我发现首先理解终止情况是有帮助的。在你的例子中,那将是
nil
。所以一旦你有了它,你就可以很容易地使用新类型创建一个对象。下面是一个基于你的代码的工作示例。减去周期: