news 2026/10/15 3:47:41

Wing 语言 Set 集合类型深度指南:标准库 API、可变性语义与 hangar 测试验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Wing 语言 Set 集合类型深度指南:标准库 API、可变性语义与 hangar 测试验证
  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

本文以 Wing 语言标准库Set/MutSet集合类型为核心,完整解读其 API 设计、不可变与可变两种语义的差异、集合相等性规则,并结合仓库内 10 个测试用例及其 hangar 测试快照,展示如何通过模拟器(sim)平台验证这些行为。读完本文,你将能熟练使用 Wing 中的集合类型进行去重存储、成员查询、复制转换与批量测试,并理解快照式测试的输出结构。

一、从一个测试快照说起

在仓库的测试工具链 hangar 中,保存着标准库测试的预期输出快照。其中tools/hangar/__snapshots__/test_corpus/sdk_tests/std/set.test.w_test_sim.md记录了set.test.w在 sim(模拟器)平台下运行wing test的完整 stdout:

pass ─ set.test.wsim » root/Default/test:add() pass ─ set.test.wsim » root/Default/test:clear() pass ─ set.test.wsim » root/Default/test:copy() pass ─ set.test.wsim » root/Default/test:copyMut() pass ─ set.test.wsim » root/Default/test:delete() pass ─ set.test.wsim » root/Default/test:equality pass ─ set.test.wsim » root/Default/test:has() pass ─ set.test.wsim » root/Default/test:mutability pass ─ set.test.wsim » root/Default/test:size() pass ─ set.test.wsim » root/Default/test:toArray() Tests 10 passed (10) Snapshots 1 skipped Test Files 1 passed (1) Duration <DURATION>

这份快照表面上是几行测试日志,实际上浓缩了 Wing 集合类型的全部核心 API:add、clear、copy、copyMut、delete、equality、has、mutability、size、toArray。以下逐一展开它们的语言语义、实现原理与测试证据。

二、Set 与 MutSet:两种可变性语义

2.1 语言层面的类型定义

在 Wing 语言参考docs/api/05-language-reference.md中,集合是六大容器类型之一:

类型名说明
Set<T>set 类型(无序的唯一元素集合,默认不可变)
MutSet<T>可变 set 类型

声明方式为字面量构造:

let z = Set<num>[1, 2, 3]; // 不可变 set let zm = MutSet<num>[1, 2, 3]; // 可变 set

核心语义:set 跟踪的是唯一值的集合——同一个值只能被加入一次。按官方 by-example 文档docs/by-example/11-sets.md的说明,Set 默认不可变(Set<T>),需要变更操作时使用MutSet<T>;重复字面量会被自动去重:

let unqiueNumbers = MutSet<num>[1, 2, 3, 3, 3]; // 实际仅包含 1、2、3 unqiueNumbers.add(4); unqiueNumbers.delete(1); let uniqueStrings = Set<str>["unique", "values", "values"]; // 去重后为 ["unique", "values"]

2.2 SDK 中的类型实现

Set与MutSet在标准库中的实现位于packages/@winglang/sdk/src/std/set.ts。文件头注释说明:这些类用于为 Wing 提供 JS 原生Set类的 JSII 子集,不应当被用户直接消费——用户在 Wing 源码中通过Set<T>/MutSet<T>语法使用,编译器负责将其映射到 SDK 类型。

两个类都声明了私有构造函数(private constructor() {}),意味着用户代码无法直接new,只能通过语言语法构造。每个类都带有_toInflightType()静态方法,通过InflightClient.forType()生成 inflight 运行时类型,保证集合类型在 preflight 与 inflight 两个阶段都能被序列化与使用。

2.3 API 总览

依据 SDK 源码与 API 参考docs/api/04-standard-library/std/set.md,两个类型对外暴露的成员如下:

Set<T>(不可变)

成员签名说明
sizenum(属性)集合长度
has(value)bool判断元素是否存在
toArray()Array生成与集合内容一致的不可变数组
copyMut()MutSet生成内容相同的可变集合

MutSet<T>(可变)

成员签名说明
sizenum(属性)集合长度
has(value)bool判断元素是否存在
add(value)MutSet向集合添加值
delete(value)bool从集合移除值;若值原本存在返回true,否则返回false
clear()void清空集合所有元素
copy()Set生成内容相同的不可变集合
toArray()Array生成与集合内容一致的不可变数组

注意add的返回值类型被声明为MutSet(便于链式调用),而delete返回布尔值标记是否真的删除了元素。

2.4 宏替换的实现细节

在packages/@winglang/sdk/src/std/set.ts中,copyMut、toArray、copy三个方法体都只是throw new Error("Macro"),真正的实现由@macro注解在编译期展开:

  • copyMut()标注为@macro new Set($self$)——编译为用原集合内容构造一个新的Set;
  • toArray()标注为@macro [...($self$)]——编译为数组展开语法,生成与原集合等价的数组;
  • MutSet.copy()同样标注为@macro new Set($self$)——把可变集合拷贝为不可变Set。

其余方法(size、has、add、delete、clear)则是抽象占位(throw new Error("Abstract")),由编译器生成的运行时 JS 代码提供真实行为,与 JS 原生Set的语义保持一致。这是 Wing“统一 preflight/inflight 模型”在容器类型上的体现:同一套 API 在两个阶段行为一致。

三、10 个测试用例逐项剖析

测试源码位于tests/sdk_tests/std/set.test.w,它同时包含顶层直接执行的断言(文件加载时即运行)与 10 个test块(由测试框架调度执行)。快照中列出的正是这 10 个 test 块。

3.1toArray():集合转数组

let mySet = Set<num> [1, 2, 3]; let myArrayFromSet = mySet.toArray(); assert(myArrayFromSet.at(0) == 1); assert(myArrayFromSet.length == mySet.size); assert(myArrayFromSet.length == 3);

覆盖场景包括:

  • 普通不可变Set<num>转数组,逐下标断言元素;
  • MutSet<str>转数组;
  • 空集合Set<bool>[]转空数组Array<bool>[];
  • 元素本身是数组的集合MutSet<Array<str>>(集合元素可以是任意类型,包括容器类型)。

这验证了toArray()的“浅拷贝不可变数组”语义:数组内容与原集合一致,且数组长度恒等于集合的size。

3.2equality:可变性不影响相等性

test "equality"{ assert(Set<num>[1, 2] == MutSet<num>[1, 2]); assert(Set<bool>[true, false] == MutSet<bool>[false, true]); }

这里直接对应语言参考docs/api/05-language-reference.md第 6.1.2 节“集合类型”的相等性规则:

  1. 两个集合相等当且仅当元素个数相同且每个元素按各自类型的相等规则一一对应;元素顺序只在Array/MutArray中有意义,集合是无序的;
  2. 集合的可变性不影响相等性——MutArray可等于内容相同的Array,同理MutSet可等于内容相同的Set;
  3. 只有同一种容器“种类”才能比较相等(Array 不能等于 Map,Set 不能与 Array 比较)。

测试中Set<bool>[true, false] == MutSet<bool>[false, true]还额外验证了集合的无序性:元素顺序不同但内容相同,依然相等。

3.3mutability:可变集合的完整操作链

test "mutability" { let openings = MutSet<str> ["A Cruel Angel's Thesis", "Lilium", "Unravel", "TOP"]; let immutOpenings: Set<str> = openings.copy(); // 可变 → 不可变 assert(immutOpenings.copyMut() == openings); // 不可变 → 可变,内容仍相等 openings.add("Abnormalize"); assert(openings.has("Abnormalize")); // 新增可查 openings.delete("TOP"); assert(openings.has("TOP") == false); // 删除后不可查 openings.clear(); assert(openings.size == 0); // 清空后大小为 0 }

这个用例把copy、copyMut、add、has、delete、clear、size串成一条完整的状态转换链路,验证了可变与不可变集合之间可以互相拷贝转换,且转换前后内容相等。

3.4size():长度统计

test "size()" { let maleVA = Set<str> ["Kenjiro Tsuda", "Akira Ishida", "Yoshitsugu Matsuoka"]; assert(maleVA.size == 3); let femaleVA = MutSet<str>["Saori Hayami", "Miyuki Sawashiro"]; assert(femaleVA.size == 2); femaleVA.add("Maaya Sakamoto"); assert(femaleVA.size == 3); // 添加后 size 增加 femaleVA.clear(); assert(femaleVA.size == 0); // 清空后 size 归零 }

同时验证不可变集合与可变集合的size属性,以及add/clear对 size 的联动影响。

3.5has():成员存在性查询

test "has()" { let genre = Set<str> ["isekai", "mecha", "cyberpunk"]; assert(genre.has("drama") == false); assert(genre.has("mecha")); let mutGenre = MutSet<str> ["rom-com", "sports", "sci-fi"]; mutGenre.delete("rom-com"); mutGenre.add("psychological"); assert(mutGenre.has("psychological")); assert(mutGenre.has("rom-com") == false); }

验证has对存在元素返回true、对不存在元素返回false,且删除后立即不可查、新增后立即可查。

3.6copyMut():不可变集合 → 可变集合

test "copyMut()" { let talkingQuirks = Set<str> ["dattebane", "battebayo", "dattebasa"]; assert(talkingQuirks.copyMut() == MutSet<str> ["dattebane", "battebayo", "dattebasa"]); }

验证Set上唯一的“转可变”入口:copyMut()产出的MutSet与原Set内容相等(再次印证可变性不影响相等性)。

3.7add():添加元素

test "add()" { let evaRebuild = MutSet<num> [1.11, 2.22, 3.33]; evaRebuild.add(3.0+1.0); // 数值表达式作为元素 assert(evaRebuild.has(3.0+1.0)); assert(evaRebuild == MutSet<num>[1.11, 2.22, 3.33, 3.0+1.0]); }

值得注意的两点:一是集合元素可以是任意表达式(这里是用3.0+1.0计算出的num),二是add之后与字面量集合直接比较相等。

3.8delete():删除元素及其返回值

test "delete()" { let studios = MutSet<str> ["Gainax", "Ghibli", "Production I.G.", "Shaft"]; assert(studios.delete("Gainax")); // 删除已存在元素 → true assert(studios.has("Gainax") == false); assert(studios.delete("Sunrise") == false); // 删除不存在元素 → false assert(studios.size == 3); }

精确验证delete的布尔返回值语义:目标值原本在集合中返回true,否则返回false(与 JSSet.prototype.delete一致)。

3.9clear():清空集合

test "clear()" { let demographics = MutSet<str> ["shounen", "shoujo", "josei", "seinen"]; demographics.clear(); assert(demographics.size == 0); demographics.add("kodomo"); demographics.clear(); // 再次清空 assert(demographics.has("kodomo") == false); }

验证clear()幂等且彻底:清空后size == 0,重新添加后再清空,元素同样不可查。

3.10copy():可变集合 → 不可变集合

test "copy()" { let acronyms = MutSet<Map<str>> [{"SEL" => "Serial Experiments Lain", "NGE" => "Neon Genesis Evangelion"}]; let copyAcronyms = acronyms.copy(); assert(copyAcronyms == Set<Map<str>>[{"SEL" => "Serial Experiments Lain", "NGE" => "Neon Genesis Evangelion"}]); acronyms.add({"DomeKano" => "Domestic na Kanojo"}); let copyAcronymsNew = acronyms.copy().copyMut(); // 链式拷贝 assert(copyAcronymsNew == acronyms); }

验证两点:copy()产出不可变Set且内容与原MutSet相等;copy().copyMut()链式转换后依然与原集合相等。同时该用例使用了MutSet<Map<str>>,证明集合元素可以是复杂容器(如Map),并且深度嵌套内容也能参与相等比较。

四、快照是如何生成的:hangar 测试框架

要理解快照文件本身,需要了解 Wing 的 E2E 测试工具 hangar(位于tools/hangar)。它对tests/sdk_tests、tests/valid、tests/doc_examples等目录下的每个.w文件自动生成测试用例并执行:

4.1 测试用例的自动生成

tools/hangar/src/generate_tests.ts递归扫描源目录中的.w文件,为每个文件生成两类 vitest 用例:

  • wing compile -t tf-aws:编译为 Terraform AWS 目标,产出set.test.w_compile_tf-aws.md这类编译快照;
  • wing test -t sim:在模拟器平台运行测试,产出_test_sim.md这类运行快照。

4.2 测试执行的实现

tools/hangar/src/generated_test_targets.ts中的testTest()负责运行wing test -t sim(只有入口文件.test.w、main.w等才会真正执行测试),把 stdout/stderr 写入fileMap,再调用createMarkdownSnapshot落盘为 Markdown 快照。这就是set.test.w_test_sim.md里stdout.log区块的来源。

4.3 快照内容为何如此“干净”

注意快照中的Duration <DURATION>——这不是神秘占位符,而是tools/hangar/src/utils.ts中sanitizeOutput()的规范化处理:为了让快照在不同机器上稳定复现,它会把测试耗时(Duration \d+m[\d.]+s)、时间戳、绝对路径、端口号、IP 等环境相关输出统一替换为<DURATION>、<TIMESTAMP>、<ABSOLUTE>、<PORT>等占位符。这也是快照工程中消除非确定性输出的标准做法。

同时,set.test.w编译到 tf-aws 平台时没有任何云资源(纯计算逻辑),所以对应的编译快照只有最小化的main.tf.json(仅含awsprovider 声明)——这从侧面说明 Set 是纯语言层面的容器类型,编译时不会产生基础设施资源。

五、如何运行这些测试

仓库中该测试可通过 hangar 生成的目标执行(由package.json/turbo.json编排),核心命令为:

# 在 hangar 包内运行 sdk_tests 相关的 sim 测试(实际命令以仓库脚本为准) wing test -t sim tests/sdk_tests/std/set.test.w

手动验证集合行为最简单的方式,是像 by-example 文档那样编写一个main.w,然后用 Wing 控制台或模拟器运行:

wing it # 本地运行 Wing 控制台 # 或 wing test -t sim main.w

在docs/by-example/11-sets.md的示例中,MutSet的toArray()与size会分别输出[2,3,4]和3,印证了 add/delete 后集合状态的变化。

六、实用要点总结

  1. 默认不可变,按需可变:Set<T>用于只读场景,需要增删时显式使用MutSet<T>;这符合 Wing 容器类型“immutable by default”的设计原则。
  2. 四个转换/读取成员:toArray()(转数组)、copy()(变不可变)、copyMut()(变可变)、has()(查询)在两类集合上按需提供,均通过@macro在编译期展开为 JS 原生操作。
  3. 相等性不看可变性与顺序:Set<num>[1, 2] == MutSet<num>[1, 2]成立,Set<num>[1, 2, 3] == Set<num>[3, 2, 1]也成立,这是语言参考第 6.1.2 节明确规定的集合语义。
  4. delete有返回值:返回true表示确实删除了元素,false表示值本就不存在,可用于防御式编程。
  5. 测试快照即文档:set.test.w_test_sim.md与set.test.w组合起来就是 Set 全部 API 的可执行规格说明——10 个用例覆盖了所有成员与关键边界(空集合、嵌套容器元素、链式拷贝、幂等清空等)。

如果需要进一步研究 API 的完整签名与参数类型,可查阅docs/api/04-standard-library/std/set.md;想深入了解相等性规则在集合上的应用,可阅读docs/api/05-language-reference.md第 6.1.2 节。

  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

相关推荐

上一篇:Diem 交易手续费模块深度解析:TransactionFee 的初始化、累积与链上销毁机制
下一篇:深入解析 TanStack Table v9 的 RowModelFns\_FeatureMap:排序、过滤与聚合函数注册表的类型中枢

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 3:47:12

如何做到 Godot 场景切换不卡顿:Godot 场景管理实战指南

如何做到 Godot 场景切换不卡顿&#xff1a;Godot 场景管理实战指南 【免费下载链接】godot-docs Godot Engine official documentation 项目地址: https://gitcode.com/GitHub_Trending/go/godot-docs 点"开始游戏"卡住 1 秒、每过一关内存往上爬、切回主界面…

作者头像 李华
网站建设 2026/10/15 3:46:24

一文读懂ResNet:残差块、网络搭建与训练优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 3:44:06

为什么外贸企业需要AIGEO优化?AI搜索时代下海外获客的新机会

为什么外贸企业需要AIGEO优化&#xff1f;AI搜索时代下海外获客的新机会核心摘要海外采购者的搜索行为正在发生根本性变化&#xff1a;越来越多人通过ChatGPT、Perplexity、Copilot等AI工具获取供应商信息&#xff0c;而非仅仅依赖传统搜索结果页。AIGEO&#xff08;AI生成引擎…

作者头像 李华
网站建设 2026/10/15 3:44:05

庆祝冬余数据联合广东省商业地产租赁协会举办“AI 赋能商业企业创新实践案例”活动取得圆满成功!

庆祝冬余数据联合广东省商业地产租赁协会举办“AI 赋能商业企业创新实践案例”活动取得圆满成功&#xff01;近日&#xff0c;由冬余数据携手广东省商业地产租赁协会及数智经济专委会共同主办的“AI赋能商业企业创新实践案例”主题活动&#xff0c;在各方与会嘉宾的热烈交流中圆…

作者头像 李华