回顾 2019

每个新年给我带来的新鲜感都不断地下降(参见我在回顾 2018 时提到的一年的相对长度),不过用一个和新的目标与决心绑定的节日来结束过于嘈杂且让人感到慵懒的假日季节并不是一个坏主意。

话说回来,看到 2020 这个数字会让人不自觉地感到兴奋,因为这在我的印象里总是存在于极为遥远的未来:我甚至不记得任何描绘 2020 年代的科幻作品。大多数作品不是止于 2010 年代,就是直接跳到 31 世纪,使得接下来的十年处于期待值的空白区域。对了,2020 年将迎来农历的庚子年,而有趣的是 维基百科 的英文直译是“金属耗子”——想不到一个传统概念会有这么朋克的名称。

2019 回放

2019 年的计划进行的非常顺利!

  • ☑ 跑 400 英里。[555/400]
  • ☑ 写 10 篇日志。[14/10]
  • ☑ 停止使用 Gmail、Inbox 应用程序。[2/2]
  • ☑ 给博客加上 rel=me 链接。
  • ☑ 进一步了解 Rust 和 Julia。[2/2]
  • ☑ 记录我中意的书籍,音乐,和影视作品。
  • ☑ 清理台式电脑。

在排除了生活中的一些不确定因素后,我在 2019 年的日常变得更有规律了。我仍然时不时地拖延博客文章,但至少发布日志的频率高了一些。学习 Rust 和 Julia 很有趣,而且我也在实际项目中用到了它们。我还尝试了用这两种语言完成 Advent of Code 并撑到了第 17 天,到解决问题所需时间太长为止。我曾考虑过圣诞节后完成所有剩余的问题,但我决定不这样做——我认为自己不会通过这些问题增加对这些语言的了解,把时间花在其他地方可能会更好。

受到 本文 启发,我给博客写了一个新的 Hugo 主题。我发现一步步消除那些可有可无的装饰的过程奇怪地令人满足:我希望博客带有任何不必要的东西。本站的订阅源已改为使用 ATOM,并且完整显示每篇日志,而不是默认的支离破碎的摘要。在上一次尝试整理浏览器书签并遭遇惨败后,我开始使用网站订阅源来阅读其他博客。到目前为止,我很喜欢这一新方式。

我今年去电影院看了三部电影,分别是《龙珠超:布罗利》,《普罗米亚》和《星球大战:天行者崛起》。三次观影都还算愉快:试图用评级来量化这种享受可能并不公平,因为我喜欢每一部电影的原因都不尽相同。由于我很少去电影院看电影,我在飞机上度过的时间贡献了我电影消费的很大一部分。《蜘蛛侠:平行宇宙》是我 2019 在飞机上看的影片里最好的一部。在圣诞夜,我没有延续大学期间的传统——看《龙与虎》——而是重温了一部我在数年前的一次飞行中看的电影,《歪小子斯科特》。这部电影的确像我记忆中的那样怪趣。

作为一个直到 2018 年都住在不会下雪的地方的人,雪对我来说是一个如此陌生的概念,以至于我会把任何有下雪场景的电影都视为圣诞节电影。即使在 2018 年年中搬到一个下雪的城市之后,我仍然没有看到过那种虽然俗套、但蓬松得似乎带有温度的圣诞雪:我所住的地方只有堆积在路边的脏水和脏冰的混合物(而且它们总能找到办法进入你的鞋子)。这更能让我想到鱼市而非圣诞节。我敢打赌,电影中所有的积雪场景都只是道具,就像登月照片一样。嗯,肯定是这样。

在 2018 年,我设置了一个 Mastodon 实例,此后我删除了自己的 Twitter 帐户,但是我并不经常使用 Mastodon。起初我觉得我属于不会写微型博客的类型,但是现在我开始认为是登录、编辑、加标签和翻译的认知开销导致了我不怎么热衷于更新状态。在 2019 年的最后几个月里,我一直在用 twtxt 格式发布微型博客,并积累了相当数量的博文(110 条)。当发布我脑海里的任何愚蠢的想法都只有一行命令之遥时,写微型博客还是挺让人上瘾的,并且重新访问 twtxt 文件有时还会给我带来新的日志想法。将一个类似 Twitter 的社交媒体服务分为只读(订阅源阅读器)、只写(也就是我使用 twtxt 的目的)和互动(我还没有很好的解决办法)的部分对我来说更加合适。我的 twtxt 文件今年很可能会取代页脚里的 Mastodon 链接,因为维持这个重量级网络应用的日常运行对我来说并不是件愉快的事情。

说到删除帐户,我已经将我所有的 Gmail 帐户设为自动转发到我自己架设的电子邮箱,并且我所有的设备现在都已经没有了 Gmail 和 Inbox(RIP)应用。我还没有做好完全放弃 Google 帐户的准备,但是我已经比之前更接近了:我在尝试使用除 Google Domains 以外的其他注册服务商,并使用 Youtube 内置的订阅源而非依赖订阅频道来获取更新信息。不过我成功删除了我的 Facebook 帐户。帐户清理还会继续下去。

2020 之路

由于这个办法在 2019 年效果很好,因此我将继续在博客文章和跑步里程上尝试与去年的数字保持一致。甜面包圈常常是我在 2019 年深夜里罪恶感的来源(都是 Dunkin'的错)。甜面包圈是为数不多的几种尽管会给健康带来不利影响但我仍然愿意食用的食物之一,所以我在此宣言:2020 年不会有甜面包圈!

我一直都想正式建立起一套遵循 3-2-1 原则的数据备份流程。至于要学习的新语言,我已经注意 Go 有一段时间了。随着 Go 模块的发布,这似乎是一个合适的开始时机。除此之外,还有即将到来的 C++20。

2020 年会有几种不同的 Linux 手机(Librem 5 和 PinePhone)发布。我很乐意尝试它们,以探索离开 Apple 生态系统的方法。另一方面,PineTime 可能是我已逐渐老化的 Pebble Time Round 的完美替代品。

除了技术性的之外,我已经有一段时间没有阅读任何书籍了,至少没有任何足以被称为文学作品的书籍。我想在 2020 年改变这种状况。

为天行者系列的终结以及我很好奇会被如何命名的 Z 世代之后出现的一代干杯!

一边眼睛凝视昨日

死者复苏!星系里回荡着神秘的讯号,这是听上去熟悉却又遥远的 我过去的容器维度大断层 的记录。

通向过去的链接

好吧,《天行者崛起》的玩笑就开到这里。如前所述,我的博客最初使用 WordPress,并于 2017-09-01 切换到 Hugo。确切地说,我实际上有两个 WordPress 博客:一个名为 Pandora(源自《无主之地 2》,但我敢肯定还有无数其他虚构的行星起了这个已经被用滥了的名字)并托管与 WordPress.com 上;另一个才是托管在 Bluehost 上的川陀大学图书室(源自艾萨克·阿西莫夫的“基地系列”)。前者使用英文,而后者用中文。由于我在删除之前保留了它们的存档,使用 此工具 稍下工夫就可以恢复那些旧日志。我避免遗漏任何一篇旧文,因为写博客的主要动机就只是为了能够随时看到过去的自我。重读那些日志是一种奇妙而又熟悉的经历:我可以清楚地看出我的某些部分发生了变化,而某些部分仍然是那个 shimmy1996。

处理日志的配图比较麻烦,而不幸的是,这些旧日志在插入图片上毫不吝惜:我选择了最简单的处理方式,只保留了原始图片,没有任何压缩或样式设置。我仍然需要一种更有效的方式来存储和提供这些图像。即使有 Git LFS 可用,我还是不愿意把超过 300 MB 的图片尽数添加到我的博客 Git 仓库中(因此它们目前处于无版本控制的状态)。多了这么多图片,部署 CDN 应该会大大改善访问速度。或许我也可以学学 Jupyter 笔记本的做法——将所有图像编码为 Base64——以将所有内容塞进同一个 HTML 文件里。

我博客的常客们(如果有的话)可能会注意到评论部分看起来有所不同:是的,我对理想中静态站点评论系统的搜索终于结束了(直到下次开始的时候)!我曾使用 WordPress、多说(已关停服务)、Disqus、Isso 作为评论系统,而现在取代它们的是我的 Hyperskip:从 Staticman 中汲取灵感,Hyperskip 将所有评论存储在 TOML 文件中,并使用电子邮件作为提交方法,以简化设置。我终于摆脱了数据库查询和外部脚本,并且将所有评论(包括 WordPress 时代的那些)转移到了和博客相同的 Git 仓库里进行版本控制。

另一位老朋友

新年刚开始一个星期,我已经切换了五次网站的配色,并还有一大堆其他方案堆在我的文件夹里(绝对不是因为 RGB 色值本身太过丑陋)。就像我对尝试 让所有字形用上正确的字体 感到精疲力尽时一样,我已决定从网站上删除所有的自定义颜色:不再显示语法高亮、花哨的按钮和暗色模式。

事实是,只要有可以调整的选项,我总会发现自己分心并花费太多时间担心那些最微不足道的对字体,颜色或间距的调整(只要看看我的日志中有多少是关于这个博客本身的就不难看出)。我发现唯一的解决方法是完全消除做出这些选择的机会,转而使用默认设置。这就是为什么我换掉了 Isso(我在试图使它和博客的外观保持一致上花了太多时间),并移除了日志标签和类别。

与俗语所说的完全相反,我并没有发现我对所舍弃的东西感到留恋。大部分时候,我的感受更近似与终于挣脱了那条绳子的大象的感觉,而不是失去了珍爱之物之后的后悔。我偶尔也会问自己,保留这些来自过去的牢骚和胡言乱语是否只是我尚未意识的另一条束缚我的绳子。对于这个问题,我的回答是:牛仔身边总少不了他的套索。

用 GNU sed 开天辟地

如果你熟悉中国神话或博客众,那么你说不定听说过被半开玩笑地称作“盘古之白”的排版习惯:在中文字符(但不包括标点符号)和拉丁字符或数字之间增加一定间隔。我所实行的这一规则的变体还包括在所有 HTML 元素(如链接和强调)的周围也加上间隔。

到目前为止,我一直在源文件(Markdown 或 org 格式)中手动添加空格:这无疑是实行这一规则最糟糕的方法。除了要费额外的工夫之外,这类排版规则还应该,在我看来,仅在输出/渲染时应用。更不用说手动调整我刚刚恢复的那一大堆旧日志不是什么吸引人的差事。因为不愿加载额外的 JavaScript,我转向了万能的 GNU sed。为了将盘古之白添加到 Hugo 生成的 HTML 和 XML 文件中(通常在 ./public 目录里),我使用了以下 shell 脚本:

#! /usr/bin/env sh
# For punctuation marks to be recongnized correctly.
export LC_CTYPE=en_US.UTF-8
find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
     -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1 \2/g' \
     -e 's/\([^[:punct:][:space:][:alnum:]]\)\([a-zA-Z0-9]\|<[a-z]\)/\1 \2/g' \
     -i {} ";"

如果你想坚持履行这一 W3C 工作草案 给出的第一选择,且并不在意生成网页的大小的话,可以换用 CSS 来生成这一间隔:

find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
     -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
     -e 's/\([^[:punct:][:space:]a-zA-Z0-9]\)\([a-zA-Z0-9]\|<[a-z]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
     -i {} ";"

如果你也是盘古之白的信徒,那么在本站留下评论时请不必担心手动添加空格:由于 Hyperskip 评论会在 Hugo 构建站点时插入,它们也会被以上的脚本影响到。请尽管坐下、放松、享受这空白一片的绝景吧。

三月结束时也和狮子一样

直到几周前(在看 Level1 News 时),我才了解到这条谚语的完整版本:“三月像狮子一样来临,像羔羊一样离去。”我是从漫画《3 月的狮子》得知前半部分的,但我当时并不知道这句话是在描述三月的天气。

以上其实就是我开始写这篇日志的的全部动机,但今年的三月的确相当不寻常。由于 COVID-19,我正在家中“远离社交”,或者说是沉浸在独处的享受中。为了准备长时间在家工作(绝对是借口),我进行了一连串的电子产品升级:我购置了第二台显示器、显示器支架和更大容量的 NAS 硬盘。实际上,去年秋天以来我一直在逐步扩展我的设备库。也许我该写一篇日志来记录这些。

我开始自己做饭是三年之前,而最近两年亚马逊的 Prime Now 服务成了我赖以果腹的唯一食材来源。自己的日常起居竟然如此依赖亚马逊的事实是有点令我担忧。但是在我拥有自己的地下基地和蓝藻农场之前,我只能凑合维持这种共生(或者应该说是寄生)关系。不过,我不确定我是否真的喜欢烹饪,至少我花在烹饪上的大部分精力都在如何减少而非增加在厨房里花费的时间。幸运的是,我几乎从不厌倦吃相同的食物,所以我一直做着相同的几道菜肴,并逐步将每一道菜的准备工作优化到最简:我的早餐是酸奶和混合干果,午餐是咖喱牛肉饭,而晚餐则是煎三文鱼配米饭和炒卷心菜。

病毒的肆虐也使我不得不暂时搁置跑步计划:我平时使用的路线已经不向公众开放。所幸在开始居家远离社交之前,我已经超前完成了不少里程(比起预订计划提前了 4 周),所以我仍然有望实现 2020 年的目标。也许是由于路上的积雪,我的跑步鞋(美津浓 Wave Rider 23)磨损得比以前更快:在累积里程达到 250 英里时,我已经感觉到跑长距离时足弓不适,而这双鞋以前的版本可以一直撑到 300 英里左右。除了鞋子问题外,随着我逐步延长的跑步时间,我时有胫骨疼痛的现象,所以我正好趁这个机会休息一阵。为了最终在工作日也能跑步,我转为了晨跑派。到目前为止,尽管有个别风雪交加的日子比较难熬(但其实挺有趣的),我仍喜欢这一决定。另外,能看到更令人振奋的日出而非日落也是个优点。

在病毒爆发期间所读到的新闻经常给我带来一种不真实感:既因为实际发生的事情,也因为新闻那种刻意挑起矛盾的报导方式。好吧,公平地说,要求一个以掠夺人们注意力为食的机构以朴实、不带修饰的方式来报导新闻本身就是一种矛盾。不过话说回来,同样从当前的情况更多地感到兴奋而非不安的我或许没有资格对新闻媒体的做法挑三拣四:仅仅想到这间普通的公寓现在是我抵御尚无治疗方法的病原体的个人要塞就足以让我睡不着觉。

如果这确实是人类覆灭的开始,至少我的博客和 Emacs 配置将由于 Github 存档计划 继续存在(假设微软没有说一套作一套的话)。在那之前,请保持安全,不要随便离开自己的胶囊生活仓,并做好准备迎接我们一直苦苦等待的荧光色太空时代藻制食品吧。

Mastodon 和 Gitea 的静态替代品

就像我当时决定换掉 Wordpress 一样,我觉得我已经受够了运行 Mastodon 和 Gitea。

跟上 Gitea 设置选项的频繁改变一直很烦人,而使用 Mastodon 时,由于系统库版本不匹配(通常是 protobuf)造成的依赖软件包出错十分常见。尽管后者不是 Mastodon 本身的错,但需要额外安装两个软件包管理器(分别用于 Ruby 和 Node.js)来运行一个程序对我来说是很荒谬的。

我于 2018 年开始在服务器上运行两者:先开始的是作为 Twitter 替代品的 Mastadon,而 Gitea 则是我后来对微软收购 Github 的回应。回头想想,相对于我的需求,这有点大材小用:我的 git 服务器和微型博客的主要用例都是单用户中心的只写任务。这意味着这些内容应以只读形式提供给我网站的访问者,而静态网页正是完美的替代品。

从 Mastadon 开始说起,我现在使用 twtxt 格式存储和发布我的微型博客。这一格式已经存在了一段时间,但最近在波浪号社区(tildeverse,一系列提供可公共访问的类 Unix 系统的网站)中开始重新流行。尽管现在有一整个旨在为该格式添加更多功能的社区支持的生态系统(其中包含各种语法扩展和软件),但我发现最基本的的时间戳加制表键加文字的语法已足够满足我的需求。这种即写即忘的形式确实很容易上瘾,尤其是与自己写的命令行客户端搭配使用时(我的版本被恰当地命名为 twixter)。

至于 Gitea,虽然我认为是 Github 的优秀替代品,但它更适合于多用户协作,而不是作为个人项目的闲置场。我决定直接自己管理 git 仓库(参阅《Pro Git》的 4.44.5 章),然后使用 stagit 生成相应的 HTML 文件。这些由 stagit 生成的页面已经替代 Gitea 作为新生的 川陀全息档案馆

在解决了我在线存在的只写任务需求后,我将继续探索剩下的两个难题:只读(内容消费)和交互(通讯方式)类操作。目前,订阅源和电子邮件是我最好的答案,但是它们仍然不足以涵盖我所有的需求。

外太空九号博客

最近,我在想办法将我的微型博客整合到当前网站里,所以我开始重新考虑 IndieWeb 所提倡的一些构想:与希望一切都通过服务器 API 和 JSON 响应来动态完成的 ActivityPub(Mastodon、Pleroma 等用于互联的协议)不同,IndieWeb 社区推荐的不少标准都支持从有正确标记的静态 HTML 文件中直接生成机器可读的网站源。IndieWeb 隐含地依赖的一大核心要素是 URI(统一资源标志符)的稳定性,或者从更高的角度来说,站点所有者对域名的控制。由于最近关于.ORG 域名的 闹剧,我逐渐意识到,一个域名昂贵到无法维护(或可能随时被扣押)的未来可能并不是遥不可及的。这会严重破坏整个 IndieWeb 赖以建立的前提,更不用说更常见的链接失效了。幸运的是,我觉得 IPFS(星际文件系统)有潜力能够解决这两个问题。

IPFS 速成班

好的,好的,我知道和一些类似的项目,例如 Dat 协议pingfs,甚至 Scruttlebutt,比起来,IPFS 的名称听起来非常不靠谱(相信我,我开始时也和你一样怀疑),而不少加密货币类创业公司将 IPFS 与各种首字母缩写词混杂在其营销资料中的事实更降低了它的可信度,但 IPFS 看起来的确是类似项目中最成熟且易于使用的。以下我对解释 IPFS 所做的尝试,其信息大部分来自 官方文档 和这一 讲座。如果你对进一步的实现细节感兴趣,这一 来自 IPFS Camp 2019 的专题讨论 是一个很好的起始点。

简单地说,一条网页链接只是指向某服务器上文件路径的一种花哨说法。就像一般的文件路径一样,服务器下线后,即使坐在同一房间的某人可能缓存了网页内容,该链接也无法被访问。在 IPFS 中,文件(或数据块)通过与其内容相应的加密哈希值作为地址,并以分布式的方式存储在所有用户群中。这意味着我们不需要中心化的设施来访问文件、可以简单地验证文件完整性、可以使用 P2P 共享来加快访问速度、以及以这种方式存储的文件内容是无法改变的。

无法更改文件内容相比我们所得到的好处来说似乎是一个相当昂贵的代价,但是就像计算机科学中的任何其他问题一样,这可以通过添加抽象层来解决。解决这一问题的 IPNS(星际域名系统)利用公钥加密来创建可以指向不同文件的不可变地址。IPNS 地址基本上就是某个公钥的哈希值。一次 IPNS 查找包括取回公钥本身、搜索具有相应的私钥签名的指针文件(一个包含 IPFS 地址的文件)、辨认最新的指针文件、以及重定向到正确的地址几个步骤。要利用 IPNS,用户首先要创建一个公私钥对,然后将公钥、想分享的文件和带有签名的指针文件上传到 IPFS 上。当需要更新时,用户只需要签署并上传新的指针文件就可以了。

IPFS 的不少方面都可以在过去的项目中看到踪影,例如 BitTorrent(P2P 共享)、Plan 9 下的 FossilVenti(一次写入的数据块和路径重定向)、和 git(哈希树/有向无环图)。但是,IPFS 的杀手级功能在于其与现有架构集成的便捷程度。专用的 HTTP 网关允许从浏览器(而不是 IPFS 客户端)中直接访问 IPFS 或 IPNS 地址,而且 IPFS 还具有与 FUSE(用户空间文件系统)的兼容性,这意味着我们甚至可以将整个 IPFS 挂载为一个只读分区:这一兼容性也让我们能够托管静态网站,但是我必须承认,访问全球(甚至星际)规模的 P2P 共享盘是个明显更酷的用法。

在 IPFS 上架设静态网站

官方指南 已经很好地概述了使用方法。以下是简要概括:

  • 运行 ipfs initipfs daemon 初始化并启动 IPFS 守护进程。
  • 生成网站文件并运行 ipfs add -r <网站文件路径> 将其内容发送到 IPFS。输出的最后几行会有路径根目录的哈希值。
  • 如果要使用 IPNS,请运行 ipfs name publish <网站根目录哈希> 以将 IPNS 链接定向到刚刚上传的文件夹上。IPNS 公钥的哈希值可以通过 ipfs key list -l 获得。
  • 在更新或重建网站文件时重复以上两个步骤。由于 IPFS 寻址过程固有的数据去重功能,该过程的实际开销并不大。对静态站点这一用例来说非常合适:越大的文件(例如照片)更新频率就越低。

完成此操作后,我们就可以从任何专用 HTTP 网关使用 <网关地址>/ipfs/<网站根目录哈希><网关地址>/ipns/<ipns-地址> 来访问刚才上传的网站了:我们可以使用由 IPFS 守护进程启动的本地网关(通常位于 127.0.0.1:8080),也可以使用 公共网关(由于 IPFS 文件取回需要在运行网关的服务器上进行,因此使用公共网关有遭受来自服务器所有者的中间人攻击的额外风险)。如果想要架设多个网站,则可以使用 ipns key gen 来生成更多 IPNS 密钥对,并在执行 ipfs name publish 时通过 --key 选项指定发布地址。

在 IPFS支持 IPNS 密钥的导入/导出 之前(这有助于我们备份密钥并从多台设备发布内容),DNSLink 可用于更方便地访问站点,但代价是需要拥有域名并信任 DNS 服务提供者。要想通过 /ipns/<域名> 从 HTTP 网关访问站点,只需为域名加入以下 TXT 记录:

dnslink=/ipfs/<网站根目录哈希>

dnslink=/ipns/<ipns-地址>

例如本站就可以通过 /ipns/shimmy1996.com(该链接使用 ipfs.io 架设的公共网关)来访问。虽然算不上是一个完全没有缺点的办法,但对我来说这是个合理的妥协。我发现 IPFS 通常比 IPNS 快,所以在 DNSLink 里用 IPFS 地址应该更加合适。为了避免每次手动复制粘贴,我在博客构建脚本中添加了以下内容以自动将网站上传到 IPFS 并更新 DNS 记录(使用 DigitalOcean 的 API):

echo "上传网站至 IPFS..."
hash=$(/usr/bin/ipfs add -Qr "<网站根目录>")

echo "更新 DNSLink 记录..."
token="<digitalocean-api-令牌>"
curl -X PUT \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer $token" \
     -d "{\"data\":\"dnslink=/ipfs/$hash\"}" \
     "https://api.digitalocean.com/v2/domains/<域名>/records/<记录-id>"

DigitalOcean 上 DNS 记录的记录 ID 也可以通过 其 API 取回,不过你可能需要在请求中增加 ?page=2 或更后面的页码才能找到你想要更新的记录。

对了,还需要注意的是,正如同使用任何脱机 HTML 文件时一样,我们需要在生成的网页中使用相对链接。在 Hugo 中,这可以通过在 config.toml 中加入

relativeURLs = true

来实现。

当然,作为一个 P2P 网络,IPFS 无法取回已经不存在于任何节点上的文件。默认情况下,IPFS 客户端会 固定 从本地计算机共享的任何内容:固定内容不会被删除,这确保 IPFS 上至少有一个可用的副本。我们可以取消固定网站的过时版本,或者,如果需要,在多台设备上查找并固定网站地址以防万一。

繁星若尘

回到 IndieWeb 的问题上:越来越黑的域名系统和链接失效使基于 HTTP 的 URI 的稳定性难以保证。但是,如果我们使用 IPFS 或 IPNS 地址作为 URI 呢?简直完美!我们通过由数学而非 FBI 警告所控制的地址获得了(理论上可以永久持续下去的)对静态网页的稳定分布式访问。消除拥有服务器的需要还降低了拥有个人网站的门槛。HTTP 协议已经存在了 29 年,而 IPFS 仅存在了 5 年。我不知道 IPFS 在接下来的 24 年中是否还会继续存在,但是如果是的话,我希望我们会看到一个或许更加混乱,但更加健壮、充满活力、多彩的在线世界。

TIReD:我的评分系统

疫情给了我更多时间来回顾想看的电影、节目和书籍,所以我考虑建立一套自己的评分系统,以简化写(如果有的话)评论的过程。

指导原则

一般的评分标准具有 10 个或更多级别。这个选择范围在我看来太大了,更不用说那些采用百分制的评分系统了。即使是最常见的五颗星系统也会在引入半颗星后变得繁琐。6 分与 7 分或 4.6 分与 5.1 分间究竟有什么区别?较高的精细度在汇总大量评分中可能会有用,但从单个评论者的角度却不是这样。我更喜欢 S1 漫区投票 所采用的方法:让评论者在更少但区别更明显的几个级别中作出选择。

我的身边统计学表明,大多数在线评分都聚集在 70%周围,这是个与 预测任何事物的成功率都为 40% 一样没用的分数。换句话说,大多数评分系统的下半部分都没有得到充分利用:我想不到任何我会给出一星半而非一星评价的情况。此外,我查看评分和评论的目的在于了解高质量的作品,而不是差的那些。一个评分系统仅关注“更好的那一半”就足够了:为什么我在忍受完一个糟糕的节目后,还要给它一个评分?《米其林指南》可没有负一颗星,不是吗?

单纯地用一个分数来概括任何东西的质量都不怎么公平。我想有一个更具表现力的评分系统,以传达我对所喜欢作品不同方面的看法。至少一个恰好对上我的波长的作品应该和一个更具大众吸引力的作品得到不一样的评价。

评分方法

隆重推出 TIReD 评分系统!以下主要以动画/电视节目为例,但这种方法中的许多内容也适用于其他艺术形式。每个作品会在以下项目中进行评分,分数之和构成最终评分:

项目 范围
有形(Tangible) 0-2
无形(Intangible) 0-2
回味(Revisit-ability) 0-1
酌情加分(Discretionary) 0-1

作品的有形方面包括视觉风格、动画、配乐、CG 质量、特殊效果等等。简单来说,作品的制作是否精良。以 0 分为起点,评分标准为:

  • 1 分:如果作品总体上吸引人,且维持了稳定的高质量、缺点极少(完美)或利用独特的想法、技术产生了非常棒的效果(高妙);
  • 2 分:作品采取的表现手法本身就足以作为观看的理由,即使作品在其他项目中得分全部为 0 分。

作品的无形方面包括故事本身、角色塑造、剧情节奏、文化背景参考等等。这些特质应该一定程度上独立于作品的具体表现介质而存在:我至少可以同样地享受基于其他艺术形式对作品的忠实再现。评分标准与之前类似,不过对于明显遵循原作内容的翻拍或改编作品,且我看过原作时,评分标准会有所调整:评分将以原作在无形方面的得分-1 为起点,视翻拍、改编的质量、难度、效果作出最多 1 分的调整后舍去超出 0-2 分的部分。例如,对+2 故事的平庸重述最多只能授予+1。翻拍和改编的起点往往比原创容易一些,所以我想以“作品本来可以有多好”为基础作出调整,对“如果我看过原作,还应该看吗”这类问题作出回应,并方便挑选出那些“不必看原作”或“已经超越了原作”的作品。

回味,顾名思义,衡量我是否想重温这一作品。这更多地与我自己的口味或怀旧之情相关:这应该是我会在一个无所事事的午后重新观看的作品。较长的作品在这一指标上会处于比较不利的地位,所以我会将特别令人难忘的片段也计算在内。在翻拍和改编的情况下,这一项目的得分大多数时候都应该仅授与我认为最好的版本。

酌情加分不应该轻易使用,且对已经在其他三个项目中获得满分的作品无效,这使得最高总得分为 5 分而不是 6 分。设置这一项目的主要目的是对于我认为作品优点没能很好地被现有评分标准体现出来时作出调整。适用的常见情况包括但不限于:

  • 类型天花板:同类最佳;
  • 作品的有形和无形方面有着紧密的联系,缺一不可;
  • 超越客观条件限制的质量,尤其是对于年代久远或预算紧张的作品。

记录格式

TIReD 评分记录的格式为 X=T/I/Re[+D]。例如:

  • 一个有形得分为 1 分、无形得分为 2 分、回味得分为 0 分、酌情加分为 0 分的作品会被记录为 3=1/2/0
  • 一个有形得分为 1 分、无形得分为 0 分、回味得分为 0 分、酌情加分为 1 分的作品会被记录为 2=1/0/0+1

对于我半途放弃看下去的作品(意味着我无法给出评分),会被标记为 DNF(did not finish)。

自我问答

我在制定 TIReD 时的一些想法碎片。

问:应该评定书籍的有形得分?

答:我觉得应该就是看行文本身的质量,例如其能否称为“文学作品”。虽然我对自己鉴定优秀作品的能力并不那么有信心,但是至少获得 2 分的作品应当比《哈利波特》要好。

问:在其他相关作品中建立的世界设定如何影响评分?

答:世界设定会影响回味得分(系统或世界是否有趣、让我想进一步了解)和无形得分(人物背景是否合乎其行为)。

问:酌情加分的规则是如何确定的?

答:不论分数范围多大,最好的作品始终会获得满分,所以给它们再加分没有什么意义。另一方面,有一些看似不起眼的作品却充满了制作团队、作者的热爱和诚意,还有一些作品存在本身就足以称为爱好者的福音。我想在保证较为客观地评判作品的有形和无形方面得分的前提下表达自己的欣赏,而酌情加分提供了一个途径。

问:在看原作前后,改编或翻拍作品的评分会如何变化?

答:在看过原作后,我将调整改编或翻拍作品的分数。

问:翻译常常会漏掉一些细节,如何处理翻译作品?

答:处理方式与翻拍相同,在看过原作后评分会被调整。

问:“TIReD”(以及分项得分名称)是怎么确定的?

答:第一个有具体名称的项目是回味(revisit-ability),后面的大部分时间都花在玩单词排列和缩写上。由于 Urban Dictionary,我差点将这套评分标准命名为“TIRD”。不过,至少不是所有的作品都是*。😜

Get GOing

我今年 完成Advent of Code!除了问题比去年(对我来说)变得简单了之外,我换用 Go 完成了今年的挑战:我觉得 Go 特别适合这类任务。

今年的谜题主要涉及字符串解析以及寻找最高效的数据结构。大部分解题流程的逻辑都比较简单,几乎不需要任何复杂的算法。

对于字符串解析,正则表达式(Go 自带支持)是最佳途径。今年为数不少的字符串解析问题意味着想只用基本的字符串操作解答会十分痛苦。我已经看够了用查找、剪切、提取子串等操作拼砌起来的怪物代码。

绝大部分时候,切片(slice)和集合(map)足够满足我的要求。Go 支持多返回值,却不支持元组(tuple);不过,我发现序列(array)和结构体(struct)基本取代了元组的功能。这些数据结构由于 Go 鼓励使用常量而非枚举类型(enum)而变得更加多功能:将所有信息储存为整形(int)使得一些捷径成为可能,同时也省去了类型转换的麻烦。和更让人安心的支持类型检查的枚举类型不同,(滥用)这些选择让用 Go 写短程序有着和行走在刀刃上(或者在 Zoom 通话时不穿裤子)一样的莫名快感。

Go 简单明了的控制流和缺少多范式支持的特点让指令式程序编写起来非常容易。不必担心是否应该使用 STL 算法或者串联迭代器方法:写个循环就好了。合理的对象可变机制也有帮助:不论是边遍历边修改集合元素,还是将带有切片的结构体传给函数,我发现我可以很快地让 Go 做我所想的事情,而不用反复翻阅语言规范。

这让我想到了 ZOI 这一经验法则:只有零、一、无穷多是合理的个数。不少我所熟悉的编程语言,例如 Python、C++、Rust,都为了追求一致性落在这一光谱的两极:一切都有着相同的规则,用户就和语言本身一样有着决定语法含义的能力。Go 有着更多的异常之处(尽管 Go 并没有异常支持)和只有“一”个的例外:内置容器神奇地有泛型支持、它们方法的返回值数量可变、除它们之外的容器都没有获得迭代支持的资格。

这只是一个不涉及 Go 其他特性(例如界面(interface)或协程(goroutine),不过这些对解决 Advent of Code 并不是必须的)的简单比较。我觉得 Go 的选择独特且有趣:一切都,嗯,不大一样