我到底什么时候需要“间接”编写递归枚举?

2023-11-26

我惊讶地发现它可以编译:

enum Foo {
    case one([Foo])
    // or
    // case one([String: Foo])
}

我本以为编译器会告诉我添加indirect对于枚举情况,如Foo包含更多Foos. Arrays and Dictionarys 都是值类型,因此这里没有间接寻址。对我来说,这在结构上类似于:

enum Foo {
    case one(Bar<Foo>)
}

// arrays and dictionaries are just a struct with either something or nothing in it
// right?
struct Bar<T> {
    let t: T?
}

...这确实需要我添加indirect到枚举。

然后我尝试了元组:

enum Foo {
    case one((Foo, Foo))
}

这也被认为是递归枚举。我认为数组和元组具有相同的内存布局,这就是为什么你可以使用将数组转换为元组withUnsafeBytes然后绑定内存...

这遵循什么规则?数组和字典实际上有什么特别之处吗?如果是这样,是否还有其他这样的“特殊”类型看起来应该需要indirect,但实际上没有?

Swift 指南不是很有帮助。它只是说:

递归枚举是一种将枚举的另一个实例作为一个或多个枚举情况的关联值的枚举。您可以通过在枚举之前编写间接来指示枚举情况是递归的,这告诉编译器插入必要的间接层。

“有另一个枚举实例”的含义相当模糊。甚至可以将其解释为case one(Bar<Foo>)不是递归的 - 该案例只有一个实例Bar<Foo>, not Foo.


我认为部分混乱源于这个假设:

我认为数组和元组具有相同的内存布局,这就是为什么您可以使用 withUnsafeBytes 将数组转换为元组,然后绑定内存...

数组和元组don't具有相同的内存布局:

  • Array<T> is a fixed-size struct with a pointer to a buffer which holds the array elements contiguously* in memory
    • 仅在本机 Swift 数组[未从 Objective-C 桥接]的情况下才保证连续性。NSArray实例不保证其底层存储是连续的,但最终这不会对下面的代码产生影响。
  • 元组是内存中连续保存的元素的固定大小缓冲区

关键是一个的大小Array<T> does not随着所持有元素的数量而变化(它的大小只是指向缓冲区的指针的大小),而元组does。元组更相当于buffer数组保存,而不是数组本身。

Array<T>.withUnsafeBytes calls Array<T>.withUnsafeBufferPointer,它返回指向缓冲区的指针,而不是数组本身。 *(在非连续桥接的情况下NSArray, _ArrayBuffer.withUnsafeBufferPointer必须创建其内容的临时连续副本为了向您返回有效的缓冲区指针。)


当为类型分配内存时,编译器需要知道类型有多大。鉴于上述情况,Array<Foo>静态地知道其大小是固定的:一个指针的大小(指向内存中其他位置的缓冲区)。

Given

enum Foo {
    case one((Foo, Foo))
}

为了布置尺寸Foo,您需要计算出其所有案例的最大尺寸。它只有一个案例,因此它的大小就是该案例的大小。

计算出尺寸one需要计算出其关联值的大小,而元素元组的大小是元素本身大小的总和(考虑到填充和对齐,但我们在这里并不真正关心这一点)。

因此,大小为Foo的大小是one,其大小为(Foo, Foo)布置在记忆里。那么,尺寸是多少(Foo, Foo)?嗯,它的大小是Foo+ 的大小Foo...每个的大小Foo+ 的大小Foo...每个的大小Foo+ 的大小Foo...

Where Array<Foo>有出路(Array<T>大小相同,无论T),我们陷入了没有基本情况的无限循环。

indirect是打破递归并为这个无限引用提供基本情况所需的关键字。它插入一个隐式的pointer通过给定case the fixed指针的大小,无论它包含什么或指向什么。这使得大小为one固定,这允许Foo具有固定的大小。

indirect较少关于Foo指的是Foo in any方式,以及更多关于允许enum 案例可能包含itself间接地(因为direct遏制会导致无限循环)。


顺便说一句,这也是为什么struct不能包含其自身的直接实例:

struct Foo {
    let foo: Foo // error: Value type 'Foo' cannot have a stored property that recursively contains it
}

会导致无限递归,而

struct Foo {
    let foo: UnsafePointer<Foo>
}

is fine.

struct不支持indirect关键字(至少在struct,您可以更直接地控制存储和布局),但已经有pitches在 Swift 论坛上添加对此的支持。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我到底什么时候需要“间接”编写递归枚举? 的相关文章

随机推荐

  • 使用 MSVC 强制导出符号

    我有一个应用程序和几个 DLL 文件中的插件 这些插件使用的符号来自 通过导出库应用程序 该应用程序链接到几个静态库 这是大多数符号的来源 只要应用程序使用符号 这就可以正常工作 如果此处未使用该符号 则在编译 DLL 时会出现链接器错误
  • float指针和int指针地址有什么区别?

    我尝试运行这段代码 int p float q q 6 6 p q 虽然这将是一个警告 但我认为 q and p大小相同 所以p可以有一个地址q 但是当我打印时 q and p我得到不同的输出 这是我的输出 p 6 600000 q 0 0
  • FireMonkey 控件的动画效果不流畅

    背景 我使用一些 FireMonkey 控件创建了一个 GUI 有些控件是动画的 它们的外观会自动更新 某些控件仅响应用户交互 滑块等 而更新 Problem 与用户控件的交互会阻止对动画控件的更新 从而导致动画不连续 有问题的动画视频 上
  • 找到可用(未加载)的 PHP 扩展

    我需要一种方法来查找所有可用的 PHP 扩展是否已加载 我在看如何查看 PHP 加载的扩展 但它只解释了如何找到加载的扩展 我也想要一种找到卸载的扩展的方法 从 php ini 知道了 extension dir 我做了一个ls exten
  • 如何使用jquery旋转插件旋转图像?

    如何使用旋转图像jQuery 旋转 plugin 我已经尝试过以下方法 但似乎不起作用
  • 使用Python请求“桥接”文件而不加载到内存中?

    我想使用Python 请求库从 URL 获取文件并将其用作发布请求中的多部分编码文件 问题是该文件可能非常大 50MB 2GB 我不想将其加载到内存中 语境here 以下文档中的示例 多部分 流下来 and 流起来 我做了这样的东西 wit
  • JavaScript 函数 parseInt() 无法正确解析以 0 开头的数字

    我在正整数之前有一些零 我想删除零 这样只保留正整数 就像 001 只会是 1 我认为最简单的方法是使用 parseInt 001 但我发现它不适用于数字 8 和 9 示例 parseInt 008 将产生 0 而不是 8 以下是完整的 h
  • Swift 动态类型检查结构?

    我对 Swift 中的动态类型检查感到困惑 具体来说 我有一个奇怪的情况 本质上我想编写 或查找 一个函数 func isInstanceOf obj Any type Any Type gt Bool 在 Objective C 中 这是
  • Python 中的交互式 BSpline 拟合

    使用以下函数 可以在输入点 P 上拟合三次样条 def plotCurve P pts np vstack P P 0 x y pts T i np arange len pts interp i np linspace 0 i max 1
  • 有没有办法检查 jekyll 站点是否在本地提供服务?

    我想将以下行添加到我的head html仅在跑步时jekyll serve本地 如果可能的话 我正在考虑使用一些简单的液体检查 当你做一个jekyll serve本地默认 jekyll environment 变量设置为 developme
  • Vue JS 数据绑定不适用于 img src

    我正在使用 vue 2 和 vue cli 3 我正在尝试绑定src标签到数据中的变量 具体来说 我正在执行以下操作 img class img time matters export default name home component
  • 错误:将 nvarchar 数据类型转换为smalldatetime 数据类型导致值超出范围

    嘿我正在尝试执行以下插入查询 SqlDataSource userQuizDataSource new SqlDataSource userQuizDataSource ConnectionString Data Source localh
  • 在 Haskell 中,如果绑定“隐藏现有绑定”,这意味着什么?

    当我编译时 我收到来自 GHC 的警告 警告 pats 的此绑定隐藏了 match ignore ancs 定义中的现有绑定 这是函数 match ignore ancs TextPat c Text t c t match ignore
  • 访问另一个 Web 应用程序的会话

    是否可以在 J2EE 应用程序 EAR 中配置两个单独的 Web 应用程序 WAR 来访问共享会话上下文 更多信息 我最终从 EAR 创建了一个共享类 它将所需的信息存储在静态成员中 这确实达到了目的 即使这看起来像是一个肮脏的黑客行为 不
  • Node.js 的 http.request 的性能如何?它可以处理多少并发请求?

    我的 node js 服务器正在使用最新的 0 4 8 http request 调用来调用另一台服务器 我使用 jMeter 来运行负载测试 每秒 50 100 个并发线程 循环 1000 次 我观察到当脚本继续运行时速度会变慢 我监控网
  • 退出应用程序时保存游戏

    我正在用 Unity C 制作一个游戏 我为游戏编写了保存本地和保存云的代码 我按下退出按钮 保存游戏然后退出 但我在这里保存的游戏有问题 当某人或玩家按下移动设备上的主页按钮时 游戏将退出并在后台运行 如果玩家重新启动设备 后台运行的游戏
  • R查找数据框中某些列与另一个列匹配的行[重复]

    这个问题在这里已经有答案了 我有一个 R 问题 我什至不知道如何用一句话来表达 但还找不到答案 我有两个数据框 我想 相交 并找到列值匹配的所有行two列 我尝试用 连接两个 intersect 和 which 语句 但都没有给我我想要的东
  • 在 multiprocessing.Pool 中调用 requests.get 时挂起

    我有以下代码 def process url url print 111 r requests get url print 222 lt never even gets here return urls to download list o
  • r - Shiny 中的 tryCatch 错误处理

    背景 我正在使用一个闪亮的应用程序cut and table一些数据 数据集包含在shiny代码如下 但头部是 gt head df in Report Year Position Target 1 2014 CEO 29 27644 2
  • 我到底什么时候需要“间接”编写递归枚举?

    我惊讶地发现它可以编译 enum Foo case one Foo or case one String Foo 我本以为编译器会告诉我添加indirect对于枚举情况 如Foo包含更多Foos Arrays and Dictionarys