一行代码千般喜
几年前我说过一个《两个字符千行泪》的故事。我在谈论软件开发时,似乎谈论的忧桑故事多一些,可能是因为别扭的事不吐不快,而喜悦的事情则可以暗自独享。今日挑选一行今年我自认为比较得意的代码,参见 Github 上的这则改动。这一行代码扫除了一个恶心了我自己两年多的问题。我把它敲出来的时候,觉得自己过去两年可能是脑子被驴踢过,为何没早想到这个呢。
还得交代一下背景故事,不然客官们几乎不大可能看懂这行代码的意义。在 servr 包中,我有一个监听本地文件变化的服务器函数
servr::httw()
,这在各类服务器程序里很常见,功能就是当服务的文件(HTML/CSS/JS/图片之类的)有更新时,就通知浏览器自动刷新页面。这就省得你每次在编辑器里编辑文件后,还得挪动鼠标去浏览器里点刷新按钮查看你的改动。你只需要把浏览器丢在旁边,只在编辑器里编辑、保存文件即可。术语上这叫实时刷新(Live-
reload)。
这个实现并不困难,我在《用 R 创建网页服务器》一文中已经略有提及,就是通过 WebSocket 让 R 与浏览器通信。浏览器每隔一段时间(比如一秒)就问问
R:你那边的文件有没有变动?R 用 list.files()
和 file.info()
算一下,若回答有,那么浏览器端就执行 JS 代码
location.reload()
刷新页面。这就是大致原理。
因为 R 需要比较当前文件信息和上一次的文件信息,所以我们需要把上一次的文件信息保存在一个变量里。前面提的那行代码中的双箭头就是干这事的:
info <<- info2
其中 info
是外层变量,用来记录上一次的文件信息,info2
是内层变量,计算当前文件信息。那以前是什么问题恶心着我自己呢?这问题是,我要实现动态编译 R Markdown 文档;当 Rmd
文件更新时,我要先把它重新编译为 HTML,然后再在浏览器里刷新这个 HTML 文件。问题就出在编译上:因为编译可能会出错,比如 R
代码还没写完整就保存导致报错;报错之后我不能下一秒再继续重新编译,因为用户可能在一秒内还修复不了错误,所以过去我用的一个机制是模仿了 Gmail
掉线之后重试的时间间隔机制,也就是先等两秒再重试,两秒后还有问题再等四秒,然后等八秒、十六秒……直到错误被修正、文档能正确编译出来。
刚开始实现这个机制时,我甚至都快为自己的机智手动点赞了,我特么真呀么是个天才呀我,居然联想到了借鉴 Gmail 的办法。然而等我真的在自己 R Markdown 文档中不慎搞出个错误来时,我立刻就觉得自己快把自己蠢哭。看到红色的错误消息每隔一秒、两秒、四秒、八秒……一次次冒出来的时候,我彻底心方了,只能手忙脚乱赶紧思考我是谁、我从哪里来、晚饭要吃什么、到底哪里写错了、咋修正,比考试还紧张。
两个月前我在捣鼓究极无限月读时,突然想到:我特么为什么要不管不顾地重试编译一个有错误的文档呢?如果编译出错,那就先等着用户修改好文档保存之后再重试呀,骚年!他要是没重新保存,那我就该按兵不动,让他有充足的时间去找出错误并修正。
之前之所以会一遍遍重试编译,是因为我在重编译之前没有刷新一下上次的文件信息变量,即上面的
info
,而是等编译成功之后才刷新,这就导致每当出错时,上一次的文件信息还是旧的(出错前的),而下次当浏览器问 R 的时候,R
仍然觉得文件有变动,所以不断重新编译一个错文档。这个问题的修正就是那么简单:在每次尝试重编译之前就刷新一下文件信息变量,成功后也照常刷新一遍,所以加一行代码就搞定了。
除了不再让用户感到莫名紧张,这个改动还让我成功扔掉了一大坨当年我觉得聪明的代码(也就是模仿 Gmail 的代码)。对码农而言,删代码永远都比添代码舒心得多。也是这个原因,今年我在 rticles 包中删掉了无数行代码(例一、例二),觉得终于把这个包归置到可维护的程度了。之前我们厂长写的代码冗余度高得令人发指,我忍了好几次终于忍不住了,大刀阔斧砍瓜切菜抽丝剥茧,把它调理成了逻辑基本统一的包,往后别人贡献新模板也更简单了。
尽管我不是故意制造问题再解决问题,但这千般喜实质上跟弗洛伊德说的那种“在寒冷的夜晚把脚伸出被窝冻一会儿再缩回来的幸福”一样。说到底,只不过是我自己先蠢到一个极致(就是蠢到觉得还有点小聪明的程度),最后又发现这个蠢可以轻易解除,从而人为造就了满足感。
作者暂无likerid, 赞赏暂由本网站代持,当作者有likerid后会全部转账给作者(我们会尽力而为)。Tips: Until now, everytime you want to store your article, we will help you store it in Filecoin network. In the future, you can store it in Filecoin network using your own filecoin.
Support author:
Author's Filecoin address:
Or you can use Likecoin to support author: