递归切片类型定义。输入 N []N

问题描述 投票:0回答:1

我在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] 被初始化时,创建一个元组数组 (ptr,len,cap),其大小与存储的元素类型无关
  • 初始化
    n[0][0]
    也是一样,结果slice of slice of slice with
    nullptr
    为最内层slice
  • 的数组指针
  • 正因为如此,
    Printf
    中的反射工作正常,并且
    n
    的值预计会从“空切片”变为“一个空切片的切片”到......
  • 那是
    n[0] = n
    布设地雷的时候:它使
    n[0]
    的ptr指向
    n
    ,在切片的嵌套中创建一个循环
  • 指示
    Printf
    找出
    n
    的类型使其递归追逐该循环并溢出堆栈。

但这一切都没有回答问题,这能用来做什么?还是应该避免的语言中的意外怪癖?

go recursion types slice
1个回答
-1
投票

我认为你被周期绊倒了。这些可能是可能的,但总的来说我认为指针是一个更好的主意。在对递归对象进行推理时,我发现首先了解终止情况会很有帮助。在你的例子中,那将是

nil
.

所以一旦你有了它,你就可以使用新类型轻松地创建一个对象。这是一个基于您的代码的工作示例,减去 周期:

package main

import "fmt"

type N []N

func main() {
   n := N{
      N{
         N{nil},
      },
   }
   fmt.Println(n) // [[[[]]]]
}
© www.soinside.com 2019 - 2024. All rights reserved.