Wing 语言 math.log2 实战:从 SDK 实现到 hangar 快照验证
Wing 语言 math.log2 实战:从 SDK 实现到 hangar 快照验证
math.log2 是 Wing 云编程语言标准库 math 模块中用于计算以 2 为底的对数的核心工具函数。本文以仓库中 log2.test.w 测试用例 及其 hangar 快照(tf-aws 编译快照、sim 运行快照)为线索,带你完整掌握 math.log2 的调用方式、边界语义、底层实现,以及 Wing 项目如何通过 hangar 快照机制同时验证 preflight 编译产物与 inflight 运行时行为。读完本文,你将能在自己的 Wing 项目中正确使用对数运算,并理解 Wing SDK 数学函数"一次编写、双阶段验证"的工程机制。
一、测试源文件:preflight 与 inflight 的双重断言
Wing 语言的核心模型是 preflight(编译期基础设施代码)与 inflight(运行期业务代码)两个阶段。位于 tests/sdk_tests/math/log2.test.w 的测试用例完美展示了这一模型:
bring math;
assert(math.log2(3) == 1.584962500721156);
assert(math.log2(2) == 1);
assert(math.log2(1) == 0);
assert(math.log2(0) == -math.INF);
test "inflight absolute" {
assert(math.log2(3) == 1.584962500721156);
assert(math.log2(2) == 1);
assert(math.log2(1) == 0);
assert(math.log2(0) == -math.INF);
}
- 文件顶部
bring math;引入标准库math模块,之后即可通过math.log2(...)静态调用; - 前 4 行
assert位于 preflight 阶段,在编译/部署基础设施时即被执行,验证数值结果; - 用
test "..." { }包裹的块是 inflight 阶段测试,运行时在模拟器或云端环境中执行,验证同一函数在运行期的行为一致性。
这种"同一断言逻辑在 preflight 与 inflight 各跑一遍"的写法,正是 Wing 测试 sdk_tests 目录下数学类用例的通用模式(同目录下还有 log.test.w、log10.test.w 等 33 个用例可对照)。
二、math.log2 的 API 签名与语义
官方 API 参考文档 docs/api/04-standard-library/math/api-reference.md 对 log2 的定义如下:
bring math;
math.log2(value: num);
| 要素 | 说明 |
|---|---|
| 所属模块 | math(Wing 标准库) |
| 调用形式 | math.log2(value: num),静态方法,无需实例化 |
参数 value |
必填,类型 num,即输入数值 |
| 返回值 | num,输入值以 2 为底的对数 |
| 官方描述 | "Returns the base-2 logarithm of a number."(返回一个数的以 2 为底的对数) |
同一 API 参考中 log 对应自然对数(base e)、log10 对应以 10 为底的对数,三者构成 Wing 数学库的完整对数函数族。
三、底层实现:直接映射 JavaScript Math.log2
math.log2 并非独立重新实现,而是封装了底层 JS 运行时的原生能力。在 packages/@winglang/sdk/src/math/math.ts 中可以看到:
/**
* Returns the base-2 logarithm of a number.
* @param value The input number.
*/
public static log2(value: number): number {
return Math.log2(value);
}
Util 是 math 模块的承载类(API 参考中称为 Math,全限定名 @winglang/sdk.math.Util),所有数学函数均为 public static 方法,因此 Wing 侧使用 math.log2(...) 即可直接调用。与 log2 相邻的 log、log10 同样是一行封装:
public static log(value: number): number {
return Math.log(value);
}
public static log10(value: number): number {
return Math.log10(value);
}
这意味着 math.log2 的行为与 ECMAScript 标准中的 Math.log2 完全一致——包括精度、舍入规则和边界值处理,测试用例中的 math.log2(3) == 1.584962500721156 正是这一底层行为的结果。
四、边界语义:math.log2(0) 与 math.INF 常量
测试用例中最值得注意的断言是 assert(math.log2(0) == -math.INF);,它验证了对数函数的负无穷边界:
- 数学上,
log2(0)趋向于-∞,JS 运行时返回-Infinity; - Wing 的
math模块为此提供了正无穷常量INF,定义于 math.ts:
/**
* Positive infinity constant
*/
public static readonly INF = Number.POSITIVE_INFINITY;
因此 -math.INF 恰好表达"负无穷"语义,可直接参与数值比较。该常量与 PI、TAU、E(分别对应 Math.PI、2 * Math.PI、Math.E)一起构成 Util 类的静态常量组,供 math.PI、math.TAU、math.E、math.INF 形式访问,同目录下的 euler.test.w、pi.test.w、tau.test.w 即针对这些常量做了专项验证。
五、tf-aws 编译快照:为什么纯计算代码没有云资源
关联文档 log2.test.w_compile_tf-aws.md 是 hangar 对 log2.test.w 执行 wing compile -t tf-aws 后生成的 Terraform JSON 快照,全文如下:
{
"//": {
"metadata": {
"backend": "local",
"stackName": "root"
},
"outputs": {}
},
"provider": {
"aws": [
{}
]
}
}
这份快照揭示了 Wing 编译模型的一个重要事实:
log2.test.w虽然bring了math标准库,但没有创建任何云资源(没有 Bucket、Queue、Function 等),因此编译产物main.tf.json中没有任何resource节点;- 整个清单仅保留
provider.aws的空配置(aws: [{}]),这是 tf-aws 平台为目标平台注入的最小必要声明,backend为local、stackName为root、无 outputs; - preflight 阶段的
assert断言在编译时即被求值,不会进入 Terraform 产物——这正是"基础设施代码与业务逻辑统一在一个语言中"的直观体现:纯计算代码不会产生无意义的云资源。
六、sim 运行快照:inflight 测试通过的真实证据
配套的 log2.test.w_test_sim.md 记录了在 sim(模拟器)平台运行 wing test 的完整输出:
pass ─ log2.test.wsim » root/Default/test:inflight absolute
Tests 1 passed (1)
Snapshots 1 skipped
Test Files 1 passed (1)
Duration <DURATION>
解读这份日志:
- 测试标识
log2.test.wsim » root/Default/test:inflight absolute表明:源文件被编译为.wsim模拟器产物,测试挂在root/Default应用树下,对应源文件中test "inflight absolute"定义的 inflight 测试; Tests 1 passed (1):唯一的 inflight 测试全部通过,证明math.log2在模拟器运行期对 3、2、1、0 四个输入值的断言全部成立;<DURATION>是快照机制刻意占位符,保证输出与执行时长无关,避免快照抖动。
七、快照背后的工程机制:hangar 如何生成并执行这些测试
这些快照并非手工维护,而是由 hangar 测试工具链自动生成。核心生成器位于 tools/hangar/src/generate_tests.ts,它扫描 tests/sdk_tests(及其他测试目录)下每个 .w 文件,自动生成一个对应的 Vitest 测试文件,其中包含两个标准测试用例:
test("wing compile -t tf-aws", async () => {
await compileTest(".../tests/sdk_tests/math", "log2.test.w", ...);
});
test("wing test -t sim", async () => {
await testTest(".../tests/sdk_tests/math", "log2.test.w", ...);
});
工作机制可以概括为:
- 递归发现:遍历
sdkTestsDir(即 tests/sdk_tests 目录),只处理扩展名为.w的源文件; - 双目标测试:对每个
.w文件生成两个用例——wing compile -t tf-aws(验证编译产物)与wing test -t sim(验证运行行为),二者分别产出_compile_tf-aws.md与_test_sim.md两种快照,这正是本文所分析的关联文档的由来; - 快照落盘:测试结果经处理后写入
tools/hangar/__snapshots__/test_corpus/目录(快照目录定义见 tools/hangar/src/paths.ts),与源文件保持相同目录结构; - 跳过控制:通过解析源文件开头的元注释(
parseMetaCommentFromPath、shouldSkipTest)支持对特定平台或环境跳过测试。
因此,当你在仓库中看到 sdk_tests/math/log2.test.w 对应两份快照时,说明该用例已经过了"tf-aws 编译产物"与"sim 运行行为"双通道的回归验证——任何对 math.log2 实现或编译器的改动,若导致数值偏差或 Terraform 清单变化,快照测试都会立即失败。
八、在本地复现:三步验证 math.log2
如果你想在本地亲手复现这套验证流程(仓库为只读,以下均为查看与运行操作):
- 直接运行 SDK 测试:进入
tests/sdk_tests/math目录,执行wing test log2.test.w,可在模拟器上看到与快照一致的1 passed结果; - 查看编译产物:执行
wing compile -t tf-aws log2.test.w,生成的main.tf.json应与本文第五节展示的快照一致(仅有provider.aws空配置); - 更新快照(可选):若有意变更后需要刷新快照,可运行 hangar 的 vitest 用例并允许快照更新,工具链会自动重写
tools/hangar/__snapshots__/下对应文件,保持快照与实现同步。
九、小结
围绕 math.log2 这一个函数,我们可以串起 Wing 工程的完整技术链路:
- 使用层:
bring math;后以math.log2(value: num)静态调用,返回num; - 语义层:直接映射 JS
Math.log2,支持log2(3)≈1.58496…、log2(2)=1、log2(1)=0等典型取值,且log2(0)返回-math.INF负无穷; - 验证层:hangar 自动生成
wing compile -t tf-aws与wing test -t sim双测试,产出 Terraform 编译快照与模拟器运行快照,确保纯计算代码在编译期不产生云资源、在运行期断言全部通过。
这一模式适用于 math 模块全部 33 个标准测试(tests/sdk_tests/math),也体现了 Wing 统一 preflight/inflight 模型下,数学库作为"与平台无关的纯函数"被严格回归测试的工程实践。当你需要在 Wing 中实现算法复杂度分析、二分查找、信息熵计算等涉及以 2 为底对数的场景时,math.log2 就是与语言运行时同源、经过完整快照验证的可靠选择。