Roc 语言 for 循环遍历 List Str 实战:从 REPL 快照测试到语言参考的完整解读
2026/9/19 20:05:47 网站建设 项目流程

Roc 语言 for 循环遍历 List Str 实战:从 REPL 快照测试到语言参考的完整解读

【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc

Roc 是一门快速、友好、函数式的系统编程语言(本仓库即其编译器实现)。本文以仓库内 REPL 快照测试 test/snapshots/repl/for_loop_list_str.md 为切入点,完整剖析 Roc 中for循环遍历List Str(字符串列表)的语法、变量可变语义、块表达式求值规则,并延伸到快照测试的 META/SOURCE/OUTPUT/PROBLEMS 四段式结构与底层生成逻辑。读完本文,你将能够读懂任意一个 REPL 快照文件、在 REPL 中熟练编写基于for的累加循环,并理解assigned \count`` 这类输出的产生原理。

一、快照文件全景:一个最小但完整的 for 循环用例

关联文档test/snapshots/repl/for_loop_list_str.md全文如下(这是理解本文所有内容的地基):

# META description=For loop iterating over List Str type=repl
» count = { var counter_ = 0 for _ in ["hello", "world", "test"] { counter_ = counter_ + 1 } counter_ }
# OUTPUT assigned `count` # PROBLEMS NIL

这个文件只有不到 30 行,却完整覆盖了 Roc 编译管线的三个关键验证维度:

  1. META 区:声明该快照的description("For loop iterating over List Str")与type=repl,告诉快照工具这是一个 REPL 类型的快照,需走 REPL 求值路径而非普通编译路径;
  2. SOURCE 区:以»提示符模拟用户在 REPL 中的输入——一个块表达式,块内先声明可变变量counter_,再用for循环遍历三个字符串,最后返回累加后的counter_
  3. OUTPUT / PROBLEMS 区:断言 REPL 求值结果——变量count被成功赋值(assigned \count`),且编译过程没有任何诊断报告(NIL`)。

从源码结构看,这属于仓库中type=repl一族的“成功路径”快照:PROBLEMSNIL表示编译与求值全程零错误、零警告,正好与 test/snapshots/README.md 中"NILmeans the compile produced no reports"的约定一致。

二、逐行拆解 SOURCE:for 循环 + 可变变量 + 块表达式的组合

2.1var counter_ = 0:可变变量声明

Roc 默认不可变,但for循环天然需要“跨迭代累积状态”,因此 Roc 提供var关键字声明可变变量。这里的命名counter_带尾下划线,是 REPL 快照中用于区分"可变容器"的常见命名风格——在 for_loop_list_bool.md(var allTrue_ = Bool.True)、for_loop_var_conditional_persist.md(var lastEven_ = 0var evenCount_ = 0)等兄弟快照中可以看到完全一致的模式。

var声明的变量可在循环体内被重新赋值,这正是 docs/langref/loops.md 所述"Loops are typically used for variable reassignment"的典型用法。

2.2for _ in ["hello", "world", "test"]:迭代器驱动 + 通配符模式

Roc 的for循环不是 C 语言那种带索引的计数器循环,而是迭代器驱动的遍历。根据 docs/langref/loops.md:

  • for的循环源可以是 range(如1..<5),也可以是任何实现了iter方法且返回Iter的类型;
  • List实现了List.iter,因此可以直接对列表做for n in [1, 2, 3, 4]
  • 循环会在每次迭代时对迭代器调用next,逐项产出元素。

这里遍历的是字符串列表["hello", "world", "test"],即List Str——本快照的 description 正是由此而来。循环变量使用_(通配符),表示“我关心迭代次数,但不关心元素本身”,这与 docs/langref/loops.md 中的示例for _ in items { $count = $count + 1 }完全一致。由于forin之间是一个 pattern,元素可以被解构或忽略,_正是"不命名该模式"的合法写法。

2.3counter_ = counter_ + 1:循环体内的副作用

循环体是一个语句块,内部执行可变变量重新赋值。每迭代一次,counter_就自增 1。因为列表恰好有 3 个元素,循环结束后counter_的值为 3。

值得注意:循环体只能包含语句,没有最终表达式,整个循环本身求值为{}(空记录类型)。所以想要从循环"带出"结果,必须依赖外部可变变量——这正是本快照counter_变量的作用。

2.4 块表达式求值:count的最终值

整个 SOURCE 是一个块表达式:

count = { var counter_ = 0 for _ in ["hello", "world", "test"] { counter_ = counter_ + 1 } counter_ }

块内最后一条表达式counter_即块的求值结果(值为 3),再通过 REPL 的顶层赋值绑定到名称count。这就是 OUTPUT 区出现assigned \count`的原因——REPL 确认了一个新定义count` 的诞生,其值为 3(循环执行了 3 次自增)。

三、OUTPUT 的底层来源:assigned \count`` 是怎么生成的

快照工具对 REPL 输入的处理逻辑位于 src/snapshot_tool/main.zig。在 该文件的编译求值路径 中可以找到关键代码:

try session.upsertDefinition(allocator, identity.kind, identity.name, input); return try std.fmt.allocPrint(allocator, "assigned `{s}`", .{identity.name});

也就是说:

  • REPL 会话先通过upsertDefinition把新定义写入会话环境(后续输入可以继续引用count);
  • 随后格式化输出assigned \名字`` 作为该输入的 REPL 响应。

这正是本快照 OUTPUT 区assigned \count`的直接生成来源。与之呼应,REPL 会话实现 [src/cli/ReplSession.zig](https://link.gitcode.com/i/f590b69eed02fe16f1bca3cb7178259b) 中也内嵌了大量同类断言(如"x = 5""assigned `x`""count = ...""assigned `count`"`),从测试层面验证了这一输出协议。

而在快照文件的约定层面,test/snapshots/README.md 明确指出:普通快照的PROBLEMS区包含诊断报告的 S 表达式序列化,NIL表示编译没有产生任何报告。本文快照的PROBLEMSNIL,即for循环遍历List Str的这段代码通过了词法、语法、canonicalization、类型检查全流程且零报告。

四、横向对照:同一家族的 for 循环快照揭示了什么

test/snapshots/repl/目录下存在一组完整的for_loop_*快照,它们从不同角度锁定 for 循环的行为,是理解本快照定位的最佳参照:

快照文件覆盖的行为点
for_loop_list_bool.md遍历List Bool,循环体内用if/else条件更新可变变量
for_loop_list_u64.md遍历List U64数值列表
for_loop_empty_list.md空列表场景,验证零次迭代时可变变量保持初值
for_loop_nested.md嵌套 for 循环
for_loop_complex_mutation.md更复杂的可变变量变更场景
for_loop_var_conditional_persist.md变量跨迭代持续存在,且按条件(奇偶判断)更新
for_loop_var_every_iteration.md每次迭代都更新的变量
for_loop_var_reassign_tracking.md对可变变量重新赋值的跟踪

从源码结构可以推断,这些快照共同验证的正是编译器对"循环 + 可变变量"的控制流分析:变量在每次迭代的读写必须被正确追踪,循环结束后块表达式读到的是最新值。本快照for_loop_list_str.md在其中承担的角色是字符串元素类型维度上的最小回归保障——确认List Str的迭代协议与List U64List Bool一致,且_通配符模式不会破坏元素遍历。

五、语言参考中的 for 循环全景

结合 docs/langref/loops.md,把本快照用到的 for 循环能力放到完整语言特性中,还有几个关键扩展点值得掌握:

5.1 遍历 Range 与自定义迭代器

除列表外,for 可直接遍历 range:

var sum = 0 for n in 1..<5 { sum = sum + n }

1..<5是左闭右开区间(不含 5),1..=5则包含 5;若边界是I64,则得到Range(I64),其迭代器产出I64值。

5.2 反向遍历

需要从尾到头遍历时,用List.iter_rev替代iter,它就地反向读取,不会像List.rev那样先构造一份反转副本:

var visited = [] for n in [1, 2, 3, 4].iter_rev() { visited = visited.append(n) } # visited 现在是 [4, 3, 2, 1]

字典(Dict)和集合(Set)也提供iter_rev按当前迭代顺序反向遍历。

5.3 模式匹配与解构

forin之间是完整模式,可内联解构元组:

var total = 0 for (x, y) in [(1, 2), (3, 4), (5, 6)] { total = total + x + y }

也可以嵌套模式;_用于不命名。需要注意模式必须穷尽——如果模式可能无法匹配运行时的某个元素(如for Ok(v) in items),会触发穷尽性相关错误;当前实现中该错误以类型不匹配形式报告,无法被类型检查器排除的非常量模式(如数字字面量模式)甚至会在运行时崩溃,因此官方建议:无法写出穷尽模式时,先用一个名字绑定整个元素,再在循环体内用match处理。

5.4breakwhile

break可立即退出最内层循环,在嵌套循环中只跳出内层;while则按布尔条件反复执行,条件必须求值为布尔值。循环体仅含语句、无最终表达式、整体求值为{}的规则对两者同样适用。

六、如何运行与验证:快照工具使用指南

本快照属于编译器仓库的自测资产,可通过快照工具实际运行验证,操作方式见 test/snapshots/README.md:

# 生成/运行全部快照 zig build run-snapshot-tool # 仅更新/运行指定快照文件 zig build run-snapshot-tool -- test/snapshots/repl/for_loop_list_str.md # 从 PROBLEMS 更新期望输出 zig build run-snapshot-tool -- test/snapshots/repl/for_loop_list_str.md --update-expected # 调试 REPL 求值过程(仅适用于 type=repl 快照,需单文件) zig build run-snapshot-tool -- test/snapshots/repl/for_loop_list_str.md --trace-eval

--trace-eval标志会开启详细的解释器跟踪,是调试 REPL 快照的利器:它在 debug 构建中默认启用,release 构建需附加-Dtrace-eval=true。快照测试的价值在于以极小的代价锁定编译器每一阶段的输出——词法、语法、canonicalization、类型检查——一旦编译器行为意外变化,对应快照立刻暴露回归。

七、从快照到实战:在 REPL 中写出可运行的 for 循环

把快照的语法迁移到日常 REPL 或 .roc 源码中,一个等价、可读性更好的计数循环是这样:

count = { var counter_ = 0 for _ in ["hello", "world", "test"] { counter_ = counter_ + 1 } counter_ }
  • 块表达式末尾的counter_是返回值,这是"循环结果带出"的标准手法;
  • 若关心元素本身,把_换成具名模式即可(如for word in words),并在循环体内基于word做处理;
  • 若需要条件更新,参考 for_loop_list_bool.md 的if/else结构,在循环体内对可变变量按条件赋值。

理解这份快照,等于同时掌握了三件事:Rocfor循环的迭代器语义、var可变变量与块表达式的组合求值方式,以及 REPL 快照测试"输入-输出-诊断"三段式断言的底层实现。对希望为 Roc 编译器贡献测试或深入 REPL 实现的人来说,test/snapshots/repl/for_loop_list_str.md及其兄弟快照是一组小而完整的入门标本。

【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询