Go语言 递归切片类型定义. type N []N

pu82cl6c  于 2023-01-18  发布在  Go
关注(0)|答案(1)|浏览(104)

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的类型会使它递归地跟踪该循环并溢出堆栈。

但所有这些都没有回答这个问题,这能用来做什么?或者这是语言中一个应该避免的无意的怪癖吗?

zysjyyx4

zysjyyx41#

我认为你在循环上被绊倒了。那些可能是可能的,但是一般来说我认为指针是一个更好的主意。当推理递归对象时,我发现首先理解终止情况是有帮助的。在你的例子中,那将是nil。所以一旦你有了它,你就可以很容易地使用新类型创建一个对象。下面是一个基于你的代码的工作示例。减去周期:

package main

import "fmt"

type N []N

func main() {
   n := N{
      N{
         N{nil},
      },
   }
   fmt.Println(n) // [[[[]]]]
}

相关问题