审计跑了三个阶段,其中三个阶段的输出长这样:clusters=0 files=0 judged=0/0 open=0 claim=AHEAD verdict_file=absent。没有任何东西被检查过,判定文件根本不存在,但审计给出的结论是:这个阶段领先于所有它本该被比较的对象。
这不是比较逻辑的bug。每一个数字都是对的。judged=0/0 是对零个对象的真实陈述,open=0 由此推导而来,而"没有未决项即视为领先"这条规则,作用在空集上时,完全按照字面意思触发了。唯一逃过这个结局的阶段纯属意外——它有603个代码块要检查,所以 open 大于零,结论根本没法形成。
![]()
分母为零时,结论是空洞的
当一个结论是从分母推导出来的时候,分母为零,这个结论就是空洞的。而空洞的结论一旦进入摘要行,就和真正挣来的结论长得一模一样。两者都打印 AHEAD。
"所有被检查的项都通过了",对一次空检查来说是真的。"没有未决项残留",在从未开启过任何项的时候也是真的。量词在这里干了读者以为该由数据来干的活。
这比普通的误报更糟的地方在于:输出里没有任何东西看起来是错的。误报有形状,你能学会去怀疑它——一个看起来好得离谱的数字,一个可疑地整齐的结果。而这个东西完全没有形状。那些零是空运行的老实汇报,结论是从这些零按一条规则推导出来的,而这条规则从来没问过:这次运行到底发生了没有。
VOID:不是通过,也不是失败
解决方案是引入第三个状态:judged = 0 或 verdict_file 缺失 → claim = VOID(未铸造)。
不是通过,不是失败,而是一个被命名的第三态,意思是"这件事没有被测量"。任何人读到这一行,都不可能把它和另外两种状态搞混。
关键点在于,VOID 不是一种更温和的失败。把一个未测量的阶段当成失败来处理,同样是错的——那会让审计因为和代码毫无关系的原因变红,然后人们会学会忽略它。三种状态对应三种不同的义务:
- 通过,意味着工作被检查过了
- 失败,意味着工作被检查过了,而且它是错的
- VOID,意味着得有人去把测量这件事变得可能
一旦空集打印 VOID,一种新的失败就变得可见了:某个阶段的分母为零,是因为枚举器坏了,而不是因为这个阶段真的是空的。VOID 对这两种情况都是诚实的回答,但其中只有一种是可以接受的。
枚举器需要自己的断言
所以枚举器需要有自己的断言,并且和判断逻辑分开:
- enumerate() → 源存在且可读(与它包含什么无关)
- judge() → 在被枚举出来的东西里,有多少通过了
第一个断言不对数量做任何声明,这正是它能安全地保持为真的原因。第二个允许返回零,而当它返回零的时候,第一个断言会告诉你:这个零是不是正确答案。
我原来有这些东西。问题从来不在数字算错了,而在于没人问过那个分母是不是该存在。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.