往墙上随手泼一桶油漆,拍张照交给文字识别软件,吐出来的乱码有 93% 的概率是一段合法的 Perl 程序。这个听起来像程序员段子的结论,来自开发者 Colin McMillen 联合虚构作者 Tim Toady 发表在卡内基梅隆大学 SIGBOVIK 2019 会议论文集第 174 页的实证研究,该文还拿下了大会颁发的 Unwitting Participation Ribbon 奖。表面看,这是对 Perl 语言形同乱码的一次公开处刑;但拆开实验设计就会发现,所谓 93% 的惊人通过率,其实是研究者在光学字符识别端做极限调参、又在代码解析端钻了历史兼容空子的产物。
这场硬核整活的起因,是 2019 年 2 月 Twitter 上的一场对话。技术专家 Adrienne Porter Felt 当时感慨自己不想太早教孩子写代码,只想让他去泥坑里踩水、往墙上抹油漆;另一位开发者 Jake Archibald 随即在评论区幽了一默:有没有可能往墙上抹了油漆,却没写出一行合法的 Perl 代码?两个月后的 4 月 1 日愚人节,McMillen 用 100 张油漆飞溅图片和一套完整的实验管线,给出了严肃的工程学回答:确实有可能不写出合法的 Perl,但翻车概率只有 7%。
12 套参数取并集:55% 的单次识别率是如何被刷到 93% 的
许多读者看完标题会误以为,随便拿张油漆图扫一下,十有八九能直接跑通 Perl。这也是该研究最核心的认知落差:93% 并不是单次识别的自然命中率,而是人为控制漏斗、把 12 种配置取并集硬凑出来的数字。
为了构建数据集,McMillen 在 Pinterest 上搜索 paint splatter wallpaper,筛掉了带有真实文字和水印的样本,攒够 100 张纯油漆泼溅图。接着他请出了开源软件 Tesseract 4.0.0-beta.1。如果按正常参数去读油漆,软件大概率只会返回空白或报错。为了让软件强行吐出字符,他在配置里动了手脚:把传统引擎的识别字母表死死锁定在可打印 ASCII 字符区间 [32, 127) 内,同时关掉了语言模型特征,取消了软件对非英语词典单词的惩罚机制。
即便把门槛降到这一步,单一配置下的表现依然算不上夸张。实验交叉测试了 3 种识别引擎(legacy、LSTM、combined legacy + LSTM)与 4 种页面分割模式(4、7、8、9),相当于每张油漆图都要过一遍 12 套不同的滤镜。在所有单一组合里,表现最好的 LSTM 引擎配合分割模式 9,成功率也只有 55%;如果按引擎把不同分割模式的结果合并,LSTM 是 74%,legacy 是 62%,combined 只有 40%。
那 93% 是怎么来的?作者定了一条宽松的规则:只要 12 种配置里有任意 1 种吐出了非空文本,且能通过 Perl 5.26.1 的语法检查,这张图就算成功;如果跑出多个合法程序,他就凭个人审美挑一个最有趣的展示(至于源码没立刻开源,他自嘲是因为自己不擅长用 GitHub)。作者在论文里坦承,这种做法本质上就是不断换参数试到跑通为止。这恰恰构成了整篇论文最妙的一层隐喻:它表面上在拿 Perl 开涮,实际上是用荒诞的实验设计,戏仿了科学研究中常见的多重比较偏差与调参刷榜(p-hacking)套路。
裸词包袱与一行严格指令:为什么乱码能算出数值 0
如果说前半程是文字识别软件在强行造词,后半程接盘的则是 Perl 语言著名的历史包袱。在程序员圈子里,Perl 素有只写语言(write-only language)的绰号,核心设计哲学是条条大路通罗马(There Is More Than One Way To Do It,缩写 TIMTOWTDI,论文虚构联名作者 Tim Toady 正是这个缩写的谐音)。为了追求写脚本时的顺手,Perl 在默认状态下允许大量的隐式类型转换和未加引号的字符串,也就是所谓的裸词(barewords)。
看看作者在截稿后补进勘误表里的几个样本,就能明白这套机制有多宽容。一张油漆图被识别成了字符串 lerzfijglpFiji-j,丢进解释器不仅没报错,还一本正经地算出了数值 0。原因在于中间那个减号触发了数值运算,减号前后的两串字母被默认当成裸词字符串,而在数值上下文里,不以数字开头的字符串会被自动转换成 0,算式就变成了 0 减 0。同理,另一张识别为 -*? 的图片也算出了 0;识别为 ;E,'__' 的图片被求值为字符串 E__;而长达一串的 ;i;c;;#\\?z{;?;;fn':.; 之所以能合法返回字符 c,全靠中间那个 # 号把后面真正致命的非法括号和乱码统统变成了单行注释。
这篇旧文在 Hacker News 社区被反复翻出来讨论(仅 2021 年 7 月 23 日那次就盖了 163 条评论,2024 年 4 月 29 日又引发一轮复盘),不少常年写 Perl 的老手出来替这门语言鸣不平。社区指出的核心漏洞在于,McMillen 在论文里虽然讨论了开启警告的 -w 参数,却只字未提现代 Perl 开发标配的 use strict 指令。
油漆能跑通,靠的不是语法通神,而是关掉了现代编译器的安检门。
在真实的工程环境里,任何合格的 Perl 程序员都会在文件开头写上 use strict;。一旦加上这行指令,解释器就会立刻翻脸,拒绝绝大多数未加引号的裸词,只给 -bareword、哈希表键名或者胖箭头 => 左侧的词留出极少数豁免通道。此外,许多油漆图被 Tesseract 扫出来的其实只是单个字母或数字,这反映的是光学字符识别软件在面对噪点图像时的幻觉分布特征,换作同样允许单行表达式求值的 Python 交互模式,单个数字照样能算作合法输入。
- 结论.对于吃瓜程序员来说,把这篇论文当成茶余饭后的段子看个乐就够了,不必真以为生产环境里的 Perl 毫无底线;而对于整天跟实验打交道的算法工程师来说,那套把 55% 硬刷到 93% 的参数并集戏法,才是真正让人后背发凉的行业黑色幽默。
