ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Roc 语言 Dict.update 详解:以 Err(Missing) 返回值为例实现键的删除语义

Roc 语言 Dict.update 详解:以 Err(Missing) 返回值为例实现键的删除语义 【免费下载链接】rocA fast, friendly, functional language.项目地址https://gitcode.com/GitHub_Trending/ro/roc点击查看免费下载本文以 Roc 仓库中的 REPL 快照文档 dict_update_remove.md 为核心骨架结合其兄弟快照与内置模块源码系统讲解Dict.update的完整语义它如何通过一个alter函数同时覆盖插入、修改、删除三种操作并重点剖析当 alter 函数返回Err(Missing)时键会被移除这一关键行为。读完本文你将掌握Dict.update的调用约定、底层哈希表实现原理以及如何用一次查找高效地完成条件性增删改。一、快照文档解读一条 REPL 测试背后的三种能力仓库中的test/snapshots/repl/目录存放了一批 REPL 快照测试每个.md文件都是一个独立的端到端用例格式统一为四段段落作用META以 ini 格式记录用例描述与类型typerepl表示这是 REPL 交互式测试SOURCE输入到 REPL 的 Roc 表达式以»提示符开头OUTPUT期望的 REPL 输出多个输出之间用---分隔PROBLEMS已知问题列表NIL表示当前无问题、测试应通过dict_update_remove.md 完整内容如下# META ~~~ini descriptionDict.update removes the key when the alter function returns Err(Missing) typerepl ~~~ # SOURCE ~~~roc » Dict.update(Dict.single(k, 10), k, |_| Err(Missing)).len() ~~~ # OUTPUT 0 # PROBLEMS NIL这条测试表达的含义非常精确对只含一个键值对(k, 10)的字典用alter |_| Err(Missing)调用Dict.update最终字典的len()为0——键k被删除了。快照中PROBLEMS为NIL说明这是当前已实现且被验证通过的稳定行为而非待办缺陷。二、Dict.update 的完整调用约定要理解上面这条测试必须先看懂Dict.update的签名与约定。其定义位于内置模块 src/build/roc/Builtin.rocupdate : Dict(k, v), k, (Try(v, [Missing]) - Try(v, [Missing])) - Dict(k, v) where [k.is_eq : k, k - Bool, k.to_hash : k, Hasher - Hasher]它接收三个参数原字典、目标键、以及一个alter函数返回处理后的新字典。关键约定有两条alter 函数收到的输入若键当前存在于字典中传入Ok(value)若键不存在传入Err(Missing)。alter 函数的返回值决定动作返回Ok(new_value)键不存在则插入键已存在则更新返回Err(Missing)键已存在则删除该键键原本就不存在则保持原状字典不变。本文主角 dict_update_remove.md 验证的正是第二条中返回Err(Missing)删除已存在键的分支。与之配套的两个兄弟快照分别验证了另外两个分支dict_update_insert.md对空字典调用Dict.update(Dict.empty(), a, |_| Ok(42))随后get(a)返回Ok(42.0)证明Err(Missing)输入 Ok(value)返回 插入dict_update_modify.md对已有键k调用Dict.update(Dict.single(k, 10), k, |_| Ok(99))随后get(k)返回Ok(99.0)证明Ok(value)输入 Ok(value)返回 更新。三个快照合在一起恰好覆盖了alter函数输入输出四种组合中的三种另外一种是键缺失 返回Err(Missing)结果是不改变字典属于平凡分支。三、源码级剖析一次查找完成三种动作Dict.update之所以比先Dict.get再Dict.insert更高效是因为它在底层实现中只做一次哈希查找并在匹配结果上直接分发到不同的数据操作。源码实现如下src/build/roc/Builtin.rocupdate |dict, key, alter| match dict { HashMap(data) match dict_find(data, key) { Found(found) match alter(Try.Ok(found.value)) { Try.Ok(new_value) { entries list_set_unsafe(data.entries, found.entry_index, (key, new_value)) HashMap({ entries, buckets: data.buckets, max_entries_before_grow: data.max_entries_before_grow, shifts: data.shifts }) } Try.Err(Missing) HashMap(dict_remove_bucket_data(data, found.bucket_index)) } Missing(missing) match alter(Try.Err(Missing)) { Try.Ok(new_value) HashMap(dict_insert_absent_data(data, missing, key, new_value)) Try.Err(Missing) dict } } }执行流程可以拆成清晰的四步一次查找dict_find(data, key)在哈希表中定位键得到Found已存在含 value 与索引或Missing不存在含可插入位置信息。无论后续走哪个分支查找都只发生这一次。键已存在 Ok(new_value)→ 原地更新用list_set_unsafe把 entries 数组中该键对应的值替换为新值其余字段原样保留构造新HashMap。这正是 dict_update_modify.md 验证的路径。键已存在 Err(Missing)→ 删除调用dict_remove_bucket_data(data, found.bucket_index)从桶数组中移除该键对应的槽位返回的新字典长度减一。这就是 dict_update_remove.md 中.len()输出0的直接原因。键不存在 Ok(new_value)→ 插入调用dict_insert_absent_data(data, missing, key, new_value)在dict_find返回的空位处写入新键值对键不存在 Err(Missing)→ 原样返回dict不产生任何分配。值得注意的是步骤 3 与 4 都复用了查找阶段获得的定位信息bucket_index/missing因此整个update的哈希计算与探测成本是单次级别的这正是文档注释所强调的比Dict.get之后再Dict.insert更高效Builtin.roc。从实现细节还能推断出字典的底层表示Dict内部是HashMap(data)其中data由entries键值对条目数组、buckets桶数组、max_entries_before_grow扩容阈值与shifts哈希移位参数四个字段构成是一个典型的开放寻址哈希表结构。dict_find、dict_insert_absent_data、dict_remove_bucket_data这些辅助函数就是围绕该结构工作的底层原语。四、与其他删除类操作的对比Dict.update并非唯一的删除途径仓库中还有专用于删除的函数便于在不同场景下选择Dict.remove直接按键删除无需 alter 函数。快照 dict_remove.md 展示了其行为——删除已存在的键后len()为1、contains(a)为False对不存在的键调用remove(missing)则字典保持不变len()仍为1。语义等价于Dict.update中alter 恒返回Err(Missing)的特例但书写更简洁。Dict.remove_all接收第二个字典删除第一个字典中键与第二个字典键相同的所有键值对集合差语义源码见 Builtin.roc快照 dict_remove_all.md 有对应测试。三者的取舍可以概括为纯删除用Dict.remove批量按集合差删除用Dict.remove_all而根据当前值决定是更新还是删除这种条件化需求则必须用Dict.update——因为它把读旧值 决策 写新值合并为一次查找避免了两次哈希访问之间的竞态窗口在纯函数式、不可变数据结构语境下则体现为时间与分配成本的节省。五、实战场景与典型模式Dict.update最典型的用法是计数器的存在性判断——根据键是否存在来决定插入初始值还是累加。内置文档示例Builtin.roc给出了一个等价的可运行模式alter |possible_value| match possible_value { Err(Missing) Ok(Bool.False) # 键不存在插入 False Ok(value) if value Err(Missing) else Ok(Bool.True) # 键存在按值决定删或更新 }而 dict_update_remove.md 的快照则揭示了一个更简洁的惯用法当需要命中即删除、未命中即忽略时可以直接传入恒返回Err(Missing)的 lambda|_| Err(Missing)配合Dict.update一次调用完成条件删除效果等价于Dict.remove但统一在同一个更新心智模型下。实际项目中常将三者组合使用例如# 键存在则累加不存在则初始化为 1计数器惯用法 Dict.update(counts, roc, |v| Ok(match v { Ok(n) n 1 Err(Missing) 1 })) # 键存在则删除不存在则原样保留幂等删除 Dict.update(counts, roc, |_| Err(Missing))六、小结一条快照、三种语义、一次查找围绕 dict_update_remove.md 这一条 REPL 快照可以完整还原 Roc 中Dict.update的设计全貌契约清晰alter以Try(v, [Missing])收尾以Try(v, [Missing])返回四种输入输出组合对应更新 / 删除 / 插入 / 不变四种结果实现高效底层只做一次dict_find查找再利用查到的索引直接更新条目、移除桶位或插入新数据Builtin.roc验证完备仓库通过 dict_update_remove.md、dict_update_insert.md、dict_update_modify.md 三份快照分别锁定了删除、插入、更新三种行为PROBLEMS均为NIL可作为可靠的参考用例继续研读。赞分享【免费下载链接】rocA fast, friendly, functional language.项目地址https://gitcode.com/GitHub_Trending/ro/roc点击查看免费下载相关推荐Roc 语言 REPL 数值解析失败路径详解以 F64.from_str 的 Err(BadNumStr) 快照测试为例Roc 语言 REPL 数值解析失败路径详解以 F64.from_str 的 Err BadNumStr 快照测试为例 F64.from_str 是 RocPuter 键值存储删除操作全解puter.kv.del() 的用法、返回值语义与源码实现Puter 键值存储删除操作全解 puter.kv.del 的用法、返回值语义与源码实现 puter.kv.del 是 Puter 开源云端操作系统的官方 J后端前端云原生3个技巧让Layerdivider成为你的AI分层魔法师3个技巧让Layerdivider成为你的AI分层魔法师 你是否曾面对一张精美的插画却苦于无法轻松分离其中的元素Layerdivider正是那个能将复杂图像创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表