blog

My blog at www.shimmy1996.com

git clone git://git.shimmy1996.com/blog.git

2020.org (79740B)

    1 #+HUGO_BASE_DIR: ../
    2 #+HUGO_SECTION: posts
    3 #+OPTIONS: author:nil
    4 #+STARTUP: fninline logdone
    5 
    6 * DONE 2019 in Review
    7 :PROPERTIES:
    8 :EXPORT_DATE: 2020-01-01
    9 :EXPORT_HUGO_CUSTOM_FRONT_MATTER: :slug 2019-in-review
   10 :END:
   11 
   12 ** DONE en
   13 :PROPERTIES:
   14 :EXPORT_TITLE: 2019 in Review
   15 :EXPORT_FILE_NAME: 2020-01-01-2019-in-review.en.md
   16 :END:
   17 
   18 Each New Year feels incrementally less special (see the relative length argument I wrote in review of 2018), although as an end to the laziness-inducing and overly noisy holiday season, having festival associated with new goals and resolutions is not too bad an idea.
   19 
   20 Looking at the number 2020 does give me a bit more excitement as it always felt like such a distant time in the future: I don't even remember any sci-fi works referencing anything in the 2020s, as most of them either stopped at the 2010s or go way beyond to the 3000s. This new decade is stuck in what I call, an expectation limbo. Oh, fun fact from [[https://en.m.wikipedia.org/wiki/2020][Wikipedia]], 2020 will also see the beginning of the year of Metal Rat in Chinese calendar - such a punk name for a something rather traditional.
   21 
   22 *** 2019 Rewind
   23 I'm glad to announce that everything went according to plan in 2019!
   24 
   25 - ☑ Run 400 miles. [555/400]
   26 - ☑ Write 10 blog posts. [14/10]
   27 - ☑ Stop using Gmail/Inbox app. [2/2]
   28 - ☑ Add =rel=me= links to blog.
   29 - ☑ Dive into Rust and Julia. [2/2]
   30 - ☑ Record books, music, and shows I enjoyed.
   31 - ☑ Clean my desktop computer.
   32 
   33 With some uncertainties in my life sorted out, I've been following my daily routines a lot better in 2019. I still procrastinate on blog posts from time to time, but at least I'm also publishing ever so more often. Learning Rust and Julia was fun, and I did get some uses out of them in real projects. I also attempted Advent of Code in both languages and got to the day 17 before each day's questions started taking too much time. I thought about finishing all the remaining ones after Christmas, but decided against it - I don't see myself learning more about the languages through these questions and the time could be better spent elsewhere.
   34 
   35 I gave the blog a face-lift, writing a new Hugo theme in the process, with [[https://getkiss.org/blog/20191004a][this article]] serving as an inspiration. I find the process of eliminating all the nice-to-haves and bells-and-whistles oddly satisfying: I enjoy having my blog as a lean, mean, killing machine. The website feed has been updated to use ATOM, with each post displayed in its entirety instead of the default broken summary. I started using web feeds for reading blogs after failing miserably at my last attempt to organize my web browser bookmarks and I've enjoyed it so far.
   36 
   37 I went to the movies three times this year, for /Dragon Ball Super: Broly/, /Promare/, and /Star Wars: The Rise of Skywalker/, and I mostly enjoyed my time in all three occasions: trying to quantify this enjoyment using a rating is probably not doing them justice, as I enjoyed the three for vastly different reasons. Since I visit the theaters so scarcely, the time I spend on airplanes actually account for a large portion of my movie consumption, and /Spider-Man: Into to the Spider-Verse/ was the stand out among those I watched while flying. On Christmas Eve, instead of /Toradora!/, an tradition of mine during college, I rewatched /Scott Pilgrim vs. the World/, which I also first saw on a flight years ago, and it was as geird (Good but in a weird way and weird but in a good way, like, you know, goodd? This is not a real word yet, but who cares? It's year of Metal Rat!) as I remembered.
   38 
   39 As someone that have always lived at places that never snow until 2018, snow to me is such a distant concept that I'd consider any movie with snow scenes a Christmas movie. Even after moving to a snowing city mid 2018, I still haven't seen the stereotypical fluffy Christmas snow that almost seem warm to the touch: all I got were piles of dirty water-ice compound on roadsides (oh and they find their way into your shoes too) that reminds me more of fish market than Christmas. Meh, I bet all those snow in the movies are just props, just like the Moon landing.
   40 
   41 Back in 2018, I set up a Mastodon instance, and I have since deleted my twitter account, but I never use Mastodon that much. At first I thought it's just that I'm not the microblogging type, but now I'm starting think it's all the cognitive overhead of logging in, editing, tagging, and translating that made posting a status update rather daunting for me. I have been microblogging in [[https://twtxt.readthedocs.io/en/latest/][twtxt]] format for the last few months of 2019 and I did rack up a considerable status count (all 110 of them). Being one command away from dumping whatever silly one-liner I have in mind is quite addicting, and revisiting the twtxt file also gives me new post ideas at times. Separating a Twitter-like social media service into read-only (with feed reader), write-only (what I use twtxt for), and interactive (still figuring that one out) parts simply works better for me. My twtxt file will likely replace Mastodon in the footer this year, as keeping a behemoth (just one more pun, please?) of a web app happy and running has not been exactly pleasant for me.
   42 
   43 Speaking of deleting accounts, I've setup forwarding from all my Gmail accounts to my self-hosted email and all my devices are Gmail and Inbox (RIP) free now. I'm still not quite ready to completely ditch the Google accounts yet, but I'm getting closer: I've been trying out registrars other than Google Domains, and using Youtube's built-in web feeds instead of relying on subscriptions. I did finally remove my Facebook account though, and the account purge will likely continue.
   44 
   45 *** Road to 2020
   46 Since it worked out really well for 2019, I'll continue to match previous year's numbers on blog posts and running. Donuts have been my guilty pleasure late night in 2019 (thanks to Dunkin'), and it's one of the few kinds of food I still willingly consume despite the detrimental effect to my health. Thus, no donuts in 2020, not under my watch!
   47 
   48 I do want to set up a proper data backup workflow, following 3-2-1 strategy and all that. As for new languages to learn, Go has been creeping on my radar for a while and with the release of Go modules, it seems like the right time to jump in. There's also C++20 on the horizon.
   49 
   50 2020 will also see the release of several Linux phones (Librem 5 and PinePhone), and I'm more than willing to try some of them out and explore ways of escaping the Apple ecosystem. PineTime, on the other hand, might be the perfect replacement for my aging Pebble Time Round.
   51 
   52 Out side of technical ones, I haven't read any books in quite a while, at least not those that can be classified as proper literature, and I seek to change that in 2020.
   53 
   54 Here's to the end of Skywalker saga, and whatever comes after generation Z!
   55 
   56 ** DONE zh
   57 :PROPERTIES:
   58 :EXPORT_TITLE: 回顾2019
   59 :EXPORT_FILE_NAME: 2020-01-01-2019-in-review.zh.md
   60 :END:
   61 
   62 每个新年给我带来的新鲜感都不断地下降(参见我在回顾2018时提到的一年的相对长度),不过用一个和新的目标与决心绑定的节日来结束过于嘈杂且让人感到慵懒的假日季节并不是一个坏主意。
   63 
   64 话说回来,看到2020这个数字会让人不自觉地感到兴奋,因为这在我的印象里总是存在于极为遥远的未来:我甚至不记得任何描绘2020年代的科幻作品。大多数作品不是止于2010年代,就是直接跳到31世纪,使得接下来的十年处于期待值的空白区域。对了,2020年将迎来农历的庚子年,而有趣的是[[https://en.m.wikipedia.org/wiki/2020][维基百科]]的英文直译是“金属耗子”——想不到一个传统概念会有这么朋克的名称。
   65 
   66 *** 2019回放
   67 2019年的计划进行的非常顺利!
   68 
   69 - ☑ 跑400英里。[555/400]
   70 - ☑ 写10篇日志。[14/10]
   71 - ☑ 停止使用Gmail、Inbox应用程序。[2/2]
   72 - ☑ 给博客加上=rel=me=链接。
   73 - ☑ 进一步了解Rust和Julia。[2/2]
   74 - ☑ 记录我中意的书籍,音乐,和影视作品。
   75 - ☑ 清理台式电脑。
   76 
   77 在排除了生活中的一些不确定因素后,我在2019年的日常变得更有规律了。我仍然时不时地拖延博客文章,但至少发布日志的频率高了一些。学习Rust和Julia很有趣,而且我也在实际项目中用到了它们。我还尝试了用这两种语言完成Advent of Code并撑到了第17天,到解决问题所需时间太长为止。我曾考虑过圣诞节后完成所有剩余的问题,但我决定不这样做——我认为自己不会通过这些问题增加对这些语言的了解,把时间花在其他地方可能会更好。
   78 
   79 受到[[https://getkiss.org/blog/20191004a][本文]]启发,我给博客写了一个新的Hugo主题。我发现一步步消除那些可有可无的装饰的过程奇怪地令人满足:我希望博客带有任何不必要的东西。本站的订阅源已改为使用ATOM,并且完整显示每篇日志,而不是默认的支离破碎的摘要。在上一次尝试整理浏览器书签并遭遇惨败后,我开始使用网站订阅源来阅读其他博客。到目前为止,我很喜欢这一新方式。
   80 
   81 我今年去电影院看了三部电影,分别是《龙珠超:布罗利》,《普罗米亚》和《星球大战:天行者崛起》。三次观影都还算愉快:试图用评级来量化这种享受可能并不公平,因为我喜欢每一部电影的原因都不尽相同。由于我很少去电影院看电影,我在飞机上度过的时间贡献了我电影消费的很大一部分。《蜘蛛侠:平行宇宙》是我2019在飞机上看的影片里最好的一部。在圣诞夜,我没有延续大学期间的传统——看《龙与虎》——而是重温了一部我在数年前的一次飞行中看的电影,《歪小子斯科特》。这部电影的确像我记忆中的那样怪趣。
   82 
   83 作为一个直到2018年都住在不会下雪的地方的人,雪对我来说是一个如此陌生的概念,以至于我会把任何有下雪场景的电影都视为圣诞节电影。即使在2018年年中搬到一个下雪的城市之后,我仍然没有看到过那种虽然俗套、但蓬松得似乎带有温度的圣诞雪:我所住的地方只有堆积在路边的脏水和脏冰的混合物(而且它们总能找到办法进入你的鞋子)。这更能让我想到鱼市而非圣诞节。我敢打赌,电影中所有的积雪场景都只是道具,就像登月照片一样。嗯,肯定是这样。
   84 
   85 在2018年,我设置了一个Mastodon实例,此后我删除了自己的Twitter帐户,但是我并不经常使用Mastodon。起初我觉得我属于不会写微型博客的类型,但是现在我开始认为是登录、编辑、加标签和翻译的认知开销导致了我不怎么热衷于更新状态。在2019年的最后几个月里,我一直在用[[https://twtxt.readthedocs.io/en/latest/][twtxt]]格式发布微型博客,并积累了相当数量的博文(110条)。当发布我脑海里的任何愚蠢的想法都只有一行命令之遥时,写微型博客还是挺让人上瘾的,并且重新访问twtxt文件有时还会给我带来新的日志想法。将一个类似Twitter的社交媒体服务分为只读(订阅源阅读器)、只写(也就是我使用twtxt的目的)和互动(我还没有很好的解决办法)的部分对我来说更加合适。我的twtxt文件今年很可能会取代页脚里的Mastodon链接,因为维持这个重量级网络应用的日常运行对我来说并不是件愉快的事情。
   86 
   87 说到删除帐户,我已经将我所有的Gmail帐户设为自动转发到我自己架设的电子邮箱,并且我所有的设备现在都已经没有了Gmail和Inbox(RIP)应用。我还没有做好完全放弃Google帐户的准备,但是我已经比之前更接近了:我在尝试使用除Google Domains以外的其他注册服务商,并使用Youtube内置的订阅源而非依赖订阅频道来获取更新信息。不过我成功删除了我的Facebook帐户。帐户清理还会继续下去。
   88 
   89 *** 2020之路
   90 由于这个办法在2019年效果很好,因此我将继续在博客文章和跑步里程上尝试与去年的数字保持一致。甜面包圈常常是我在2019年深夜里罪恶感的来源(都是Dunkin'的错)。甜面包圈是为数不多的几种尽管会给健康带来不利影响但我仍然愿意食用的食物之一,所以我在此宣言:2020年不会有甜面包圈!
   91 
   92 我一直都想正式建立起一套遵循3-2-1原则的数据备份流程。至于要学习的新语言,我已经注意Go有一段时间了。随着Go模块的发布,这似乎是一个合适的开始时机。除此之外,还有即将到来的C++20。
   93 
   94 2020年会有几种不同的Linux手机(Librem 5和PinePhone)发布。我很乐意尝试它们,以探索离开Apple生态系统的方法。另一方面,PineTime可能是我已逐渐老化的Pebble Time Round的完美替代品。
   95 
   96 除了技术性的之外,我已经有一段时间没有阅读任何书籍了,至少没有任何足以被称为文学作品的书籍。我想在2020年改变这种状况。
   97 
   98 为天行者系列的终结以及我很好奇会被如何命名的Z世代之后出现的一代干杯!
   99 
  100 * DONE Staring at Yesterday in One Eye
  101 :PROPERTIES:
  102 :EXPORT_DATE: 2020-01-06
  103 :EXPORT_HUGO_SLUG: staring-at-yesterday-in-one-eye
  104 :END:
  105 
  106 ** DONE en
  107 :PROPERTIES:
  108 :EXPORT_FILE_NAME: 2020-01-06-staring-at-yesterday-in-one-eye.en.md
  109 :EXPORT_TITLE: Staring at Yesterday in One Eye
  110 :END:
  111 
  112 The dead speak! The galaxy has heard a mysterious broadcast, a record of THE *GREAT DIMENSIONAL FRACTURE* in the familiar yet distant tone of *MY PREVIOUS VESSEL*.
  113 
  114 *** A Link to the Past
  115 Alright, that's enough puns and /The Rise of Skywalker/ jokes from me. As mentioned before, my blog was originally using WordPress and switched to Hugo in 2017-09-01. To be more specific, I actually had two WordPress blogs: one named Pandora (because of /Borderlands 2/, but this is such an cliche name that I'm sure there's a million other imaginary planets using this name) hosted with WordPress.com, and another being Library of Trantor (because of Isaac Asimov's /Foundation/ series) hosted on Bluehost, with the former written in English and latter in Chinese. Since I kept archives of both before taking them down, I was able to revive all those old posts from the grave using [[https://github.com/SchumacherFM/wordpress-to-hugo-exporter][this tool]] and some elbow grease. I refrained myself from leaving out any of the old posts, as the main motivation of this effort is really just to be able to easily see and be reminded of my younger self. It's a strange yet familiar experience reading those old writings: I can see parts of me that has changed and parts that are still distinctively shimmy1996.
  116 
  117 Handling images is tricky and my old posts made quite liberal uses of them unfortunately: I opted for the simplest way out and just kept the originals without any kind of fancy compression or styling. I still need to figure out a more efficient way to both store and serve those images. Even with Git LFS available, I was reluctant to add over 300 MB of images to my blog repository (so they are currently in untracked-land), and now my blog could definitely benefit from a CDN setup. Perhaps I could also do what Jupyter notebooks do—encode all images in Base64—to get a single HTML file.
  118 
  119 For the regular visitors of my blog out there (if there are any), you might notice that the comment section looks different: that's right, the search for the ideal static site commenting system is finally over for me (until it starts again)! Through out the years, I've used WordPress, Duoshuo (now defunct), Disqus, and Isso as my comment systems. Now, my [[https://git.shimmy1996.com/hugo-hyperskip][Hyperskip]] has superseded them all: taking inspiration from [[https://staticman.net/][Staticman]], I set up Hyperskip to store all the comments in a TOML file, and opted to use email as the submission method, simplifying the setup. Gone are the days of databases, queries, and external scripts, and I get to migrate and version control all the comments (including the ones from the WordPress era) in the same Git repository as my blog too.
  120 
  121 *** The Other Old Friend
  122 One week into the New Year and I have already switched color scheme of the website five times, with another dozen sitting in my folder (totally not because of how unsightly these RGB color codes are). Much like how I'm a bit burned-out from [[/en/posts/2019-12-01-fun-with-fonts-on-the-web/][getting fonts right for everything]], I have decided to remove all custom color choices from the website: no more syntax highlighting, fancy buttons, nor dark modes.
  123 
  124 As long as there are little knobs that I can toy around with, I always find myself distracted and spending way too much time worrying about the most insignificant choice of words, colors, or spacing (as you can tell by how much of I blog about the blog). The only cure I found is to simply remove the opportunities of making those choices altogether, and stick with the default. This is why I replaced Isso (I spent too much time trying to make it not look so foreign) and tags and categories are now gone, too.
  125 
  126 Completely contrast to how the saying normally goes, I hardly ever find myself missing the things I cut away. More often than not, I sympathize with the elephant that finally broke free from the rope, rather than the remorse after losing something cherished. I do occasionally ask myself whether maintaining all those babbling by my past self is just another such rope holding me back that I just haven't realized yet. Well, my response is: a cowboy could always use a lasso on the road.
  127 
  128 ** DONE zh
  129 :PROPERTIES:
  130 :EXPORT_FILE_NAME: 2020-01-06-staring-at-yesterday-in-one-eye.zh.md
  131 :EXPORT_TITLE: 一边眼睛凝视昨日
  132 :END:
  133 
  134 死者复苏!星系里回荡着神秘的讯号,这是听上去熟悉却又遥远的*我过去的容器*对*维度大断层*的记录。
  135 
  136 *** 通向过去的链接
  137 好吧,《天行者崛起》的玩笑就开到这里。如前所述,我的博客最初使用WordPress,并于2017-09-01切换到Hugo。确切地说,我实际上有两个WordPress博客:一个名为Pandora(源自《无主之地2》,但我敢肯定还有无数其他虚构的行星起了这个已经被用滥了的名字)并托管与WordPress.com上;另一个才是托管在Bluehost上的川陀大学图书室(源自艾萨克·阿西莫夫的“基地系列”)。前者使用英文,而后者用中文。由于我在删除之前保留了它们的存档,使用[[https://github.com/SchumacherFM/wordpress-to-hugo-exporter][此工具]]稍下工夫就可以恢复那些旧日志。我避免遗漏任何一篇旧文,因为写博客的主要动机就只是为了能够随时看到过去的自我。重读那些日志是一种奇妙而又熟悉的经历:我可以清楚地看出我的某些部分发生了变化,而某些部分仍然是那个shimmy1996。
  138 
  139 处理日志的配图比较麻烦,而不幸的是,这些旧日志在插入图片上毫不吝惜:我选择了最简单的处理方式,只保留了原始图片,没有任何压缩或样式设置。我仍然需要一种更有效的方式来存储和提供这些图像。即使有Git LFS可用,我还是不愿意把超过300 MB的图片尽数添加到我的博客Git仓库中(因此它们目前处于无版本控制的状态)。多了这么多图片,部署CDN应该会大大改善访问速度。或许我也可以学学Jupyter笔记本的做法——将所有图像编码为Base64——以将所有内容塞进同一个HTML文件里。
  140 
  141 我博客的常客们(如果有的话)可能会注意到评论部分看起来有所不同:是的,我对理想中静态站点评论系统的搜索终于结束了(直到下次开始的时候)!我曾使用WordPress、多说(已关停服务)、Disqus、Isso作为评论系统,而现在取代它们的是我的[[https://git.shimmy1996.com/hugo-hyperskip][Hyperskip]]:从[[https://staticman.net/][Staticman]]中汲取灵感,Hyperskip将所有评论存储在TOML文件中,并使用电子邮件作为提交方法,以简化设置。我终于摆脱了数据库查询和外部脚本,并且将所有评论(包括WordPress时代的那些)转移到了和博客相同的Git仓库里进行版本控制。
  142 
  143 *** 另一位老朋友
  144 新年刚开始一个星期,我已经切换了五次网站的配色,并还有一大堆其他方案堆在我的文件夹里(绝对不是因为RGB色值本身太过丑陋)。就像我对尝试[[/zh/posts/2019-12-01-fun-with-fonts-on-the-web/][让所有字形用上正确的字体]]感到精疲力尽时一样,我已决定从网站上删除所有的自定义颜色:不再显示语法高亮、花哨的按钮和暗色模式。
  145 
  146 事实是,只要有可以调整的选项,我总会发现自己分心并花费太多时间担心那些最微不足道的对字体,颜色或间距的调整(只要看看我的日志中有多少是关于这个博客本身的就不难看出)。我发现唯一的解决方法是完全消除做出这些选择的机会,转而使用默认设置。这就是为什么我换掉了Isso(我在试图使它和博客的外观保持一致上花了太多时间),并移除了日志标签和类别。
  147 
  148 与俗语所说的完全相反,我并没有发现我对所舍弃的东西感到留恋。大部分时候,我的感受更近似与终于挣脱了那条绳子的大象的感觉,而不是失去了珍爱之物之后的后悔。我偶尔也会问自己,保留这些来自过去的牢骚和胡言乱语是否只是我尚未意识的另一条束缚我的绳子。对于这个问题,我的回答是:牛仔身边总少不了他的套索。
  149 
  150 * DONE Becoming Pangu with GNU sed
  151 :PROPERTIES:
  152 :EXPORT_DATE: 2020-01-09
  153 :EXPORT_HUGO_SLUG: becoming-pangu-with-sed
  154 :END:
  155 
  156 ** DONE en
  157 :PROPERTIES:
  158 :EXPORT_FILE_NAME: 2020-01-09-becoming-pangu-with-gnu-sed.en.md
  159 :EXPORT_TITLE: Becoming Pangu with GNU sed
  160 :END:
  161 
  162 In case you aren't familiar with Chinese mythology or blogosphere, there's an old meme aptly named "Space of Pangu": a typesetting rule of thumb in favor of additional spacing between Chinese characters (but not punctuation marks) and Latin characters or numbers. My variant of the rule also includes additional spacing around any HTML elements like links and emphasis.
  163 
  164 Up till now, I've been manually adding spaces in my source files (in Markdown or org), which is admittedly the worst way to do it. Aside from the additional chore, such a typesetting rule should, in my opinion, be implemented in the output/rendering format, not the source. Besides, manually fixing all the old posts I just brought back is not exactly a rewarding task. Unwilling to load additional [[https://github.com/vinta/pangu.js][JavaScript]], I turned to the all-mighty GNU sed. To add Space of Pangu to the final HTML and XML files that Hugo produces (normally in the =./public= directory), I used the following shell script:
  165 #+BEGIN_SRC sh
  166   #! /usr/bin/env sh
  167   # For punctuation marks to be recongnized correctly.
  168   export LC_CTYPE=en_US.UTF-8
  169   find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
  170        -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1 \2/g' \
  171        -e 's/\([^[:punct:][:space:][:alnum:]]\)\([a-zA-Z0-9]\|<[a-z]\)/\1 \2/g' \
  172        -i {} ";"
  173 #+END_SRC
  174 
  175 In case you are adamant about adhering to the recommendation by this [[https://www.w3.org/TR/clreq/#mixed_text_composition_in_horizontal_writing_mode][W3C Working Draft]] and wouldn't mind bloating up the resulting web page, using CSS to create the spacing should do the trick:
  176 #+BEGIN_SRC sh
  177   find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
  178        -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
  179        -e 's/\([^[:punct:][:space:]a-zA-Z0-9]\)\([a-zA-Z0-9]\|<[a-z]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
  180        -i {} ";"
  181 #+END_SRC
  182 
  183 If you are another one of those Space of Pangu disciples, just note that there's no need to worry about adding spaces when leaving comments here: thanks to [[https://git.shimmy1996.com/hugo-hyperskip][Hyperskip]] comments being inserted at Hugo's building stage, they are affected by those scripts as well. Just sit back, relax, and enjoy staring at the blank spaces.
  184 
  185 ** DONE zh
  186 :PROPERTIES:
  187 :EXPORT_FILE_NAME: 2020-01-09-becoming-pangu-with-gnu-sed.zh.md
  188 :EXPORT_TITLE: 用GNU sed开天辟地
  189 :END:
  190 
  191 如果你熟悉中国神话或博客众,那么你说不定听说过被半开玩笑地称作“盘古之白”的排版习惯:在中文字符(但不包括标点符号)和拉丁字符或数字之间增加一定间隔。我所实行的这一规则的变体还包括在所有HTML元素(如链接和强调)的周围也加上间隔。
  192 
  193 到目前为止,我一直在源文件(Markdown或org格式)中手动添加空格:这无疑是实行这一规则最糟糕的方法。除了要费额外的工夫之外,这类排版规则还应该,在我看来,仅在输出/渲染时应用。更不用说手动调整我刚刚恢复的那一大堆旧日志不是什么吸引人的差事。因为不愿加载额外的[[https://github.com/vinta/pangu.js][JavaScript]],我转向了万能的GNU sed。为了将盘古之白添加到Hugo生成的HTML和XML文件中(通常在=./public=目录里),我使用了以下shell脚本:
  194 #+BEGIN_SRC sh
  195   #! /usr/bin/env sh
  196   # For punctuation marks to be recongnized correctly.
  197   export LC_CTYPE=en_US.UTF-8
  198   find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
  199        -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1 \2/g' \
  200        -e 's/\([^[:punct:][:space:][:alnum:]]\)\([a-zA-Z0-9]\|<[a-z]\)/\1 \2/g' \
  201        -i {} ";"
  202 #+END_SRC
  203 
  204 如果你想坚持履行这一[[https://www.w3.org/TR/clreq/#mixed_text_composition_in_horizontal_writing_mode][W3C工作草案]]给出的第一选择,且并不在意生成网页的大小的话,可以换用CSS来生成这一间隔:
  205 #+BEGIN_SRC sh
  206   find . -path "./public/*" \( -name "*.html" -or -name "*.xml" \) -print -exec sed \
  207        -e 's/\([a-zA-Z0-9]\|<\/[a-z]*>\)\([^[:punct:][:space:]a-zA-Z0-9\s]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
  208        -e 's/\([^[:punct:][:space:]a-zA-Z0-9]\)\([a-zA-Z0-9]\|<[a-z]\)/\1<span style="margin:0.25ch;"><\/span>\2/g' \
  209        -i {} ";"
  210 #+END_SRC
  211 
  212 如果你也是盘古之白的信徒,那么在本站留下评论时请不必担心手动添加空格:由于[[https://git.shimmy1996.com/hugo-hyperskip][Hyperskip]]评论会在Hugo构建站点时插入,它们也会被以上的脚本影响到。请尽管坐下、放松、享受这空白一片的绝景吧。
  213 
  214 * DONE March Goes out Like a Lion, Too
  215 :PROPERTIES:
  216 :EXPORT_DATE: 2020-03-31
  217 :EXPORT_HUGO_SLUG: march-goes-out-like-a-lion-too
  218 :END:
  219 
  220 ** DONE en
  221 :PROPERTIES:
  222 :EXPORT_FILE_NAME: 2020-03-31-march-goes-out-like-a-lion-too.en.md
  223 :EXPORT_TITLE: March Goes out Like a Lion, Too
  224 :END:
  225 
  226 It was not until a few weeks ago (while watching Level1 News) that I learned about the complete version of the saying "March comes in like a lion and goes out like a lamb." I knew the first half of the saying from manga series /March Comes in Like a Lion/, but I had no idea the saying was describing the weather in March.
  227 
  228 What you have read so far was actually my entire motivation for starting this post, but it has indeed been a rather unusual March. Because of COVID-19, I'm spending time at home "social distancing", or rather, indulging myself in the company of solitude. In preparation for (a.k.a. using as an excuse) extended periods of working from home, I went on an upgrade spree for electronics: I got a second monitor, a monitor stand, and larger hard drives for my NAS. In fact, I've been gradually expanding my arsenal of devices since last Fall, so look out for a potential setup post.
  229 
  230 Amazon's Prime Now service has been keeping me fed for two out of the past three years during which I cooked for myself. It's a bit alarming that Amazon of all things has become literally something I can't live without. But until I have my underground bunker and algae farm, I'll have to make do with this symbiotic (or should I say parasitic) relationship. I'm not sure if I really enjoy cooking though, as least most of my efforts devoted to it has been on how to reduce the amount of time I spend in the kitchen. Fortunately I hardly ever get tired of eating the same dishes, so I just kept making the same ones, while gradually optimizing the preparation: I have yogurt and trail mix for breakfast, beef curry with rice for lunch, and pan-fried salmon with rice and stir-fried cabbage for dinner.
  231 
  232 The pandemic also puts my running plans on hold: the trail I normally run on has been closed down. I did get plenty of mileages in before social distancing started (4 weeks ahead of schedule in terms of total mileage now), so I should still be on track to hit my 2020 target. Perhaps due to the snow and ice along the way, my running shoes (Mizuno Wave Rider 23) are wearing out faster than before: at 250-mile-mark, I'm already feeling arch discomfort in longer distance runs, while previous iterations of those shoes lasted until around 300 miles. Aside from shoe issues, shin pain also started to creep up as I've been doing longer runs, so this just might be the opportunity I needed to take some rest. I have converted myself to a morning runner as I plan to ultimately sneak a run or two on weekdays. So far I'm enjoying my morning routines, despite a few of snow-stormy days that were extra tough (but fun). Plus, I get to see sunrise instead of its less cheerful sibling.
  233 
  234 Reading the news during the outbreak frequently struck me with an unreal feeling: because of both the things that are actually happening and the way news articles covers them in a deliberately divisive facade. To be fair, asking an organization that preys on human attention to report in a plain and down-to-earth way is an oxymoron in itself. It's probably hypocritical for me to pick on the news agencies though, as I am also guilty of deriving excitement from the current situation: the mere thought that what is ordinarily just an apartment is now my personal fortress against an uncured pathogen is enough to keep me up at night.
  235 
  236 Should this indeed be the downfall of humanity, at least my blog and Emacs configuration will (assuming Microsoft means it) live on thanks to the [[https://archiveprogram.github.com/][Github Archive Program]]. Before that, be safe, stay at your personal living pods, and prepare for the neon-colored Space-Age algae diet we've all been waiting for.
  237 
  238 ** DONE zh
  239 :PROPERTIES:
  240 :EXPORT_FILE_NAME: 2020-03-31-march-goes-out-like-a-lion-too.zh.md
  241 :EXPORT_TITLE: 三月结束时也和狮子一样
  242 :END:
  243 
  244 直到几周前(在看Level1 News时),我才了解到这条谚语的完整版本:“三月像狮子一样来临,像羔羊一样离去。”我是从漫画《3月的狮子》得知前半部分的,但我当时并不知道这句话是在描述三月的天气。
  245 
  246 以上其实就是我开始写这篇日志的的全部动机,但今年的三月的确相当不寻常。由于COVID-19,我正在家中“远离社交”,或者说是沉浸在独处的享受中。为了准备长时间在家工作(绝对是借口),我进行了一连串的电子产品升级:我购置了第二台显示器、显示器支架和更大容量的NAS硬盘。实际上,去年秋天以来我一直在逐步扩展我的设备库。也许我该写一篇日志来记录这些。
  247 
  248 我开始自己做饭是三年之前,而最近两年亚马逊的Prime Now服务成了我赖以果腹的唯一食材来源。自己的日常起居竟然如此依赖亚马逊的事实是有点令我担忧。但是在我拥有自己的地下基地和蓝藻农场之前,我只能凑合维持这种共生(或者应该说是寄生)关系。不过,我不确定我是否真的喜欢烹饪,至少我花在烹饪上的大部分精力都在如何减少而非增加在厨房里花费的时间。幸运的是,我几乎从不厌倦吃相同的食物,所以我一直做着相同的几道菜肴,并逐步将每一道菜的准备工作优化到最简:我的早餐是酸奶和混合干果,午餐是咖喱牛肉饭,而晚餐则是煎三文鱼配米饭和炒卷心菜。
  249 
  250 病毒的肆虐也使我不得不暂时搁置跑步计划:我平时使用的路线已经不向公众开放。所幸在开始居家远离社交之前,我已经超前完成了不少里程(比起预订计划提前了4周),所以我仍然有望实现2020年的目标。也许是由于路上的积雪,我的跑步鞋(美津浓Wave Rider 23)磨损得比以前更快:在累积里程达到250英里时,我已经感觉到跑长距离时足弓不适,而这双鞋以前的版本可以一直撑到300英里左右。除了鞋子问题外,随着我逐步延长的跑步时间,我时有胫骨疼痛的现象,所以我正好趁这个机会休息一阵。为了最终在工作日也能跑步,我转为了晨跑派。到目前为止,尽管有个别风雪交加的日子比较难熬(但其实挺有趣的),我仍喜欢这一决定。另外,能看到更令人振奋的日出而非日落也是个优点。
  251 
  252 在病毒爆发期间所读到的新闻经常给我带来一种不真实感:既因为实际发生的事情,也因为新闻那种刻意挑起矛盾的报导方式。好吧,公平地说,要求一个以掠夺人们注意力为食的机构以朴实、不带修饰的方式来报导新闻本身就是一种矛盾。不过话说回来,同样从当前的情况更多地感到兴奋而非不安的我或许没有资格对新闻媒体的做法挑三拣四:仅仅想到这间普通的公寓现在是我抵御尚无治疗方法的病原体的个人要塞就足以让我睡不着觉。
  253 
  254 如果这确实是人类覆灭的开始,至少我的博客和Emacs配置将由于[[https://archiveprogram.github.com/][Github存档计划]]继续存在(假设微软没有说一套作一套的话)。在那之前,请保持安全,不要随便离开自己的胶囊生活仓,并做好准备迎接我们一直苦苦等待的荧光色太空时代藻制食品吧。
  255 
  256 * DONE Static Alternatives to Mastodon and Gitea
  257 :PROPERTIES:
  258 :EXPORT_DATE: 2020-04-04
  259 :EXPORT_HUGO_SLUG: static-alternatives-to-mastodon-and-gitea
  260 :END:
  261 
  262 ** DONE en
  263 :PROPERTIES:
  264 :EXPORT_FILE_NAME: 2020-04-04-static-alternatives-to-mastodon-and-gitea.en.md
  265 :EXPORT_TITLE: Static Alternatives to Mastodon and Gitea
  266 :END:
  267 
  268 Like how I decided to switch off Wordpress, I think I've had enough running Mastodon and Gitea.
  269 
  270 Keeping up with configuration changes with Gitea had been annoying, whereas with
  271 Mastodon, breakages are common due the mismatching system library versions
  272 (mostly protobuf) in the dependencies. While the latter is not a fault of
  273 Mastodon itself, having to install two package managers (for Ruby and Node.js,
  274 respectively) just to run a program is rather ridiculous to me.
  275 
  276 I started hosting both applications in 2018: Mastadon first as a replacement for
  277 Twitter, and Gitea later in reaction to Microsoft's acquisition of
  278 Github. Looking back, they were probably overkill for my needs: my primary use
  279 case for a git server and a micro blog are both very much single-user focused
  280 and write-only, which means these content should be available in read-only form
  281 for my site's visitors, making static pages the perfect replacement for both web
  282 front ends.
  283 
  284 Starting with Mastadon, I'm using the [[https://twtxt.readthedocs.io/en/latest/][twtxt]] format to store and serve my micro
  285 blog. The format has existed for some time now, but enjoyed a recent resurgence
  286 in the tildeverse (a series of websites offering public access Unix-like
  287 systems). While there is now a whole community supported ecosystem of various
  288 syntax extensions and software seeking to add more features to the format, I
  289 have found the barebone timestamp-tab-and-then-text syntax to be sufficient. The
  290 write-and-forget cycle is really addicting, and even more so when using a
  291 command line client (mine is aptly named [[https://git.shimmy1996.com/twixter/][twixter]]).
  292 
  293 As for Gitea, while an excellent Github replacement in my opinion, is more
  294 suitable for community collaboration than as a personal project dumping
  295 ground. I opted to manage the git repositories directly (see Chapter [[https://git-scm.com/book/en/v2/Git-on-the-Server-Setting-Up-the-Server][4.4]] and [[https://git-scm.com/book/en/v2/Git-on-the-Server-Git-Daemon][4.5]]
  296 of /Pro Git/), and use [[https://git.codemadness.org/stagit/][stagit]] to generate the corresponding HTML files. These
  297 stagit-generated pages have replaced Gitea as the new [[https://git.shimmy1996.com/][Trantor Holocron]].
  298 
  299 Now that I have found satisfactory solution for the write-only portion of my
  300 online presence, I will continue to explore options for the remaining two
  301 pillars: read-only (content consumption) and interaction (means of
  302 communication). Web feeds and email are my best answers now, but they still
  303 don't cover all the bases in my experience.
  304 
  305 ** DONE zh
  306 :PROPERTIES:
  307 :EXPORT_FILE_NAME: 2020-04-04-static-alternatives-to-mastodon-and-gitea.zh.md
  308 :EXPORT_TITLE: Mastodon和Gitea的静态替代品
  309 :END:
  310 
  311 就像我当时决定换掉Wordpress一样,我觉得我已经受够了运行Mastodon和Gitea。
  312 
  313 跟上Gitea设置选项的频繁改变一直很烦人,而使用Mastodon时,由于系统库版本不匹配(通常是protobuf)造成的依赖软件包出错十分常见。尽管后者不是Mastodon本身的错,但需要额外安装两个软件包管理器(分别用于Ruby和Node.js)来运行一个程序对我来说是很荒谬的。
  314 
  315 我于2018年开始在服务器上运行两者:先开始的是作为Twitter替代品的Mastadon,而Gitea则是我后来对微软收购Github的回应。回头想想,相对于我的需求,这有点大材小用:我的git服务器和微型博客的主要用例都是单用户中心的只写任务。这意味着这些内容应以只读形式提供给我网站的访问者,而静态网页正是完美的替代品。
  316 
  317 从Mastadon开始说起,我现在使用[[https://twtxt.readthedocs.io/en/latest/][twtxt]]格式存储和发布我的微型博客。这一格式已经存在了一段时间,但最近在波浪号社区(tildeverse,一系列提供可公共访问的类Unix系统的网站)中开始重新流行。尽管现在有一整个旨在为该格式添加更多功能的社区支持的生态系统(其中包含各种语法扩展和软件),但我发现最基本的的时间戳加制表键加文字的语法已足够满足我的需求。这种即写即忘的形式确实很容易上瘾,尤其是与自己写的命令行客户端搭配使用时(我的版本被恰当地命名为[[https://git.shimmy1996.com/twixter/][twixter]])。
  318 
  319 至于Gitea,虽然我认为是Github的优秀替代品,但它更适合于多用户协作,而不是作为个人项目的闲置场。我决定直接自己管理git仓库(参阅《Pro Git》的[[https://git-scm.com/book/zh/v2/%25E6%259C%258D%25E5%258A%25A1%25E5%2599%25A8%25E4%25B8%258A%25E7%259A%2584-Git-%25E9%2585%258D%25E7%25BD%25AE%25E6%259C%258D%25E5%258A%25A1%25E5%2599%25A8][4.4]]和[[https://git-scm.com/book/zh/v2/%25E6%259C%258D%25E5%258A%25A1%25E5%2599%25A8%25E4%25B8%258A%25E7%259A%2584-Git-Git-%25E5%25AE%2588%25E6%258A%25A4%25E8%25BF%259B%25E7%25A8%258B][4.5]]章),然后使用[[https://git.codemadness.org/stagit/][stagit]]生成相应的HTML文件。这些由stagit生成的页面已经替代Gitea作为新生的[[https://git.shimmy1996.com/][川陀全息档案馆]]。
  320 
  321 在解决了我在线存在的只写任务需求后,我将继续探索剩下的两个难题:只读(内容消费)和交互(通讯方式)类操作。目前,订阅源和电子邮件是我最好的答案,但是它们仍然不足以涵盖我所有的需求。
  322 
  323 * DONE Blog 9 from Outer Space
  324 :PROPERTIES:
  325 :EXPORT_HUGO_SLUG: blog-9-from-outer-space
  326 :END:
  327 
  328 ** DONE en
  329 CLOSED: [2020-04-10 Fri 01:27]
  330 :PROPERTIES:
  331 :EXPORT_FILE_NAME: 2020-04-10-blog-9-from-outer-space.en.md
  332 :EXPORT_TITLE: Blog 9 from Outer Space
  333 :END:
  334 
  335 Recently, I've been thinking about ways to unify my micro blog entries with my current site, and I've been reconsidering the ideas from [[https://indieweb.org/][IndieWeb]]: unlike [[https://activitypub.rocks/][ActivityPub]] (the protocol Mastodon, Pleroma and the likes use for federation), which seems to want everything be done dynamically via server APIs and JSON responses, the various standards recommended by the IndieWeb community allows machine readable feed to be generated straight from a static HTML file correctly marked-up. A core idea that IndieWeb seem to implicitly rely on is the lifetime of the URIs, and to a greater extent, site owner's control over the domain name. Withe the recent [[https://www.eff.org/deeplinks/2020/03/members-congress-once-again-urge-icann-save-dot-org][drama]] regarding the .ORG domain, I came to realize that a future in which domain names are too expensive to maintain (or are subject to seizures by various entities) may not actually be too distant, and this could seriously undermine the entire premise IndieWeb is built upon, not to mention the a lot more common link rots. Fortunately, I think the [[https://ipfs.io/][IPFS]] (InterPlanetary File System) has the potential to solve both problems.
  336 
  337 *** A Crash Course on IPFS
  338 Now, now, I know when compared similar projects like the [[https://dat.foundation/][Dat protocol]], [[https://github.com/yarrick/pingfs][pingfs]], or even [[https://scuttlebutt.nz/][Scruttlebutt]], IPFS has a really buzz-wordy vibe (trust me, I was as skeptical as you are at the beginning) to it, and the various cryptocurrency start-ups that bundle IPFS and all kinds of acronyms in their marketing materials surely doesn't do it any favors, but it does seem like the most established and ready-to-use. Here's my best attempt at explaining IPFS, with information mostly obtained from the [[https://docs.ipfs.io/][official documentation]] and [[https://www.youtube.com/watch?v=HUVmypx9HGI][this talk]]. In case you are interested in further implementation details, [[https://www.youtube.com/watch?v=Z5zNPwMDYGg][this session from IPFS Camp 2019]] is a great starting point.
  339 
  340 A simplified interpretation of link to an web page is but a fancy way to point to a file on some server. Just like path to a file, the link would be unreachable if the server is down, even if someone sitting in the same room might have the contents cached. In IPFS, files (or data blocks) are addressed by corresponding cryptographic hashes of their contents, and stored in a distributed fashion across all peers. This means no centralized facility is required to access the files, file integrity can be easily verified, P2P sharing can be used to speed up access, and files stored this way are inherently immutable.
  341 
  342 Not being able to change files seems like a rather large price to pay, but just like any other problem in computer science, this can be solved by adding a layer of abstraction. IPNS (InterPlanetary Name System) utilizes public-key cryptography to create immutable addresses that can point to different files. An IPNS address is basically the hash of a public key. An IPNS lookup would involve retrieval of the public key, searching for files (each containing an IPFS address) signed by the corresponding private keys, identifying the most recent one, and finally redirecting to the correct file. To utilize IPNS, the user would start by creating a public-private key pair, followed by uploading desired files into IPNS, and sign and upload a pointer file containing IPFS address to the uploaded content. When an update is desired, the user only need to sign and upload another pointer file to the new location.
  343 
  344 A lot of ideas used in IPFS has been explored before by projects like BitTorrent (peer-to-peer sharing), [[https://en.wikipedia.org/wiki/Fossil_(file_system)][Fossil]] and [[https://en.wikipedia.org/wiki/Venti][Venti]] from Plan9 (write-once data blocks and path redirection), git (Merkle tree/directed acyclic graph), etc. However, the killer feature is how easily IPFS integrates with existing infrastructure. Not only are there HTTP gateways that allows for accessing IPFS/IPNS from web browsers instead of IPFS clients, but also compatibility with FUSE (Filesystem in Userspace), which actually allows you to mount the entire IPFS as a read-only partition: sure this also makes hosting static websites possible, but you have to admit that having access to a global-scale (or should I say, interplanetary?) P2P shared drive is way cooler.
  345 
  346 *** Hosting Static Websites on IPFS
  347 The [[https://docs-beta.ipfs.io/how-to/command-line-quick-start/][official guide]] already outlines the general usage pattern pretty well. Here's the TLDR:
  348 - Run =ipfs init= and =ipfs daemon= to initialize and start the IPFS client.
  349 - Generate the website files and run =ipfs add -r <website-root>= to send its contents onto the IPFS. The last few lines of the output should tell you the hash for the root directory.
  350 - If you want to make use of IPNS, run =ipfs name publish <website-root-hash>= to direct the IPNS link to the folder you just uploaded. The IPNS public key hash can be obtained via =ipfs key list -l=.
  351 - Repeat the last two steps every time and the website files are updated or rebuilt. The process has little overhead due to the inherent deduplication in addressing, making it particularly suitable for static sites where larger files (like photos) tend to change less often.
  352 
  353 Once this is done, you can access your website at either =<gatway-address>/ipfs/<website-root-hash>= or =<gatway-address>/ipns/<ipns-address>= from any HTTP gateway: you can use the local one (likely at =127.0.0.1:8080=) started by the IPFS daemon, or any of [[https://ipfs.github.io/public-gateway-checker/][the public ones]] (comes with extra risk of MITM attacks from the gateway owners as file retrieval is done on the gateway servers). In case you have multiple websites, you can generate more IPNS key pairs using =ipns key gen=, and specify =--key= when running =ipfs name publish= to a specific IPNS address.
  354 
  355 Before IPFS [[https://github.com/ipfs/go-ipfs/issues/4240][supports import/export of the IPNS keys]] though (so that we can backup keys and publish from multiple devices), [[https://docs.ipfs.io/guides/concepts/dnslink/][DNSLink]] can be used to more conveniently maintain access to a site, albeit at the cost of depending on owning a domain name and trusting the DNS host provider. To allow access to the site from the gateways via =/ipns/<domain-name>=,
  356 simply add a TXT record to the domain:
  357 #+begin_example
  358   dnslink=/ipfs/<website-root-hash>
  359 #+end_example
  360 or
  361 #+begin_example
  362   dnslink=/ipns/<ipns-address>
  363 #+end_example
  364 For instance, you can now access this site using at [[https://ipfs.io/ipns/shimmy1996.com/][=/ipns/shimmy1996.com=]] (this is a link using the ipfs.io gateway). While not flawless, to me this is a reasonable compromise for now. I find find IPFS to be generally faster than IPNS, so using IPFS address with DNSLink probably makes more sense. To avoid manually copy-pasting the IPFS address each time, I added to my blog build script the following to automatically upload website to IPFS and update DNS record (using [[https://developers.digitalocean.com/documentation/v2/#update-a-domain-record][DigitalOcean's API]]):
  365 #+BEGIN_SRC sh
  366   echo "Uploading to IPFS..."
  367   hash=$(/usr/bin/ipfs add -Qr "<website-root>")
  368 
  369   echo "Updating DNSLink record..."
  370   token="<digitalocean-api-token>"
  371   curl -X PUT \
  372        -H "Content-Type: application/json" \
  373        -H "Authorization: Bearer $token" \
  374        -d "{\"data\":\"dnslink=/ipfs/$hash\"}" \
  375        "https://api.digitalocean.com/v2/domains/<domain>/records/<record-id>"
  376 #+END_SRC
  377 Record ID for DNS records on DigitalOcean can also be [[https://developers.digitalocean.com/documentation/v2/#list-all-domain-records][retrieved via their API]]. You may need to add =?page=2= or later to the request to find the record you want.
  378 
  379 Do note that like using any offline HTML files, we need to use relative URLs in the generated web pages. In Hugo, this can be achieved by setting
  380 #+BEGIN_SRC toml
  381   relativeURLs = true
  382 #+END_SRC
  383 in =config.toml=.
  384 
  385 Of course, being a P2P network, IPFS won't be able to retrieve the files if there is no copy to work with at all. By default, IPFS client would [[https://docs.ipfs.io/guides/concepts/pinning/][pin]] anything you shared from the local machine: pinned contents won't get deleted, ensuring at least one copy of the shared content is available on IPFS. You can unpin outdated versions of the website, or if you want, find and pin the shared directory on multiple machines for some redundancy.
  386 
  387 *** The Stars, Like Dust
  388 Back to the issue with IndieWeb: the increasingly shady domain name system and link rots makes URI stability in HTTP hard to maintain. However, what if we use IPFS/IPNS addresses as URIs? It's a match made in heaven: we get robust distributed access to static web pages, gated by Mathematics instead of FBI warnings, that can theoretically last forever. Removing the need for maintaining a server also lowers the barrier of entry of owning a website. The HTTP protocol has existed for 29 years, and IPFS, only 5. I don't know if IPFS will continue to exist for the next 24 years to come, but if it does, I hope we will be looking at a perhaps more chaotic, but more robust, lively, and colorful, online world.
  389 
  390 ** DONE zh
  391 CLOSED: [2020-04-10 Fri 09:53]
  392 :PROPERTIES:
  393 :EXPORT_FILE_NAME: 2020-04-10-blog-9-from-outer-space.zh.md
  394 :EXPORT_TITLE: 外太空九号博客
  395 :END:
  396 
  397 最近,我在想办法将我的微型博客整合到当前网站里,所以我开始重新考虑[[https://indieweb.org/][IndieWeb]]所提倡的一些构想:与希望一切都通过服务器API和JSON响应来动态完成的[[https://activitypub.rocks/][ActivityPub]](Mastodon、Pleroma等用于互联的协议)不同,IndieWeb社区推荐的不少标准都支持从有正确标记的静态HTML文件中直接生成机器可读的网站源。IndieWeb隐含地依赖的一大核心要素是URI(统一资源标志符)的稳定性,或者从更高的角度来说,站点所有者对域名的控制。由于最近关于.ORG域名的[[https://www.eff.org/deeplinks/2020/03/members-congress-once-again-urge-icann-save-dot-org][闹剧]],我逐渐意识到,一个域名昂贵到无法维护(或可能随时被扣押)的未来可能并不是遥不可及的。这会严重破坏整个IndieWeb赖以建立的前提,更不用说更常见的链接失效了。幸运的是,我觉得[[https://ipfs.io/][IPFS]](星际文件系统)有潜力能够解决这两个问题。
  398 
  399 *** IPFS 速成班
  400 好的,好的,我知道和一些类似的项目,例如[[https://dat.foundation/][Dat协议]],[[https://github.com/yarrick/pingfs][pingfs]],甚至[[https://scuttlebutt.nz/][Scruttlebutt]],比起来,IPFS的名称听起来非常不靠谱(相信我,我开始时也和你一样怀疑),而不少加密货币类创业公司将IPFS与各种首字母缩写词混杂在其营销资料中的事实更降低了它的可信度,但IPFS看起来的确是类似项目中最成熟且易于使用的。以下我对解释IPFS所做的尝试,其信息大部分来自[[https://docs.ipfs.io/][官方文档]]和这一[[https://www.youtube.com/watch?v=HUVmypx9HGI][讲座]]。如果你对进一步的实现细节感兴趣,这一[[https://www.youtube.com/watch?v=Z5zNPwMDYGg][来自IPFS Camp 2019的专题讨论]]是一个很好的起始点。
  401 
  402 简单地说,一条网页链接只是指向某服务器上文件路径的一种花哨说法。就像一般的文件路径一样,服务器下线后,即使坐在同一房间的某人可能缓存了网页内容,该链接也无法被访问。在IPFS中,文件(或数据块)通过与其内容相应的加密哈希值作为地址,并以分布式的方式存储在所有用户群中。这意味着我们不需要中心化的设施来访问文件、可以简单地验证文件完整性、可以使用P2P共享来加快访问速度、以及以这种方式存储的文件内容是无法改变的。
  403 
  404 无法更改文件内容相比我们所得到的好处来说似乎是一个相当昂贵的代价,但是就像计算机科学中的任何其他问题一样,这可以通过添加抽象层来解决。解决这一问题的IPNS(星际域名系统)利用公钥加密来创建可以指向不同文件的不可变地址。IPNS地址基本上就是某个公钥的哈希值。一次IPNS查找包括取回公钥本身、搜索具有相应的私钥签名的指针文件(一个包含IPFS地址的文件)、辨认最新的指针文件、以及重定向到正确的地址几个步骤。要利用IPNS,用户首先要创建一个公私钥对,然后将公钥、想分享的文件和带有签名的指针文件上传到IPFS上。当需要更新时,用户只需要签署并上传新的指针文件就可以了。
  405 
  406 IPFS的不少方面都可以在过去的项目中看到踪影,例如BitTorrent(P2P共享)、Plan 9下的[[https://zh.wikipedia.org/zh-cn/Fossil_(%25E6%25AA%2594%25E6%25A1%2588%25E7%25B3%25BB%25E7%25B5%25B1)][Fossil]]和[[https://en.wikipedia.org/wiki/Venti][Venti]](一次写入的数据块和路径重定向)、和git(哈希树/有向无环图)。但是,IPFS的杀手级功能在于其与现有架构集成的便捷程度。专用的HTTP网关允许从浏览器(而不是IPFS客户端)中直接访问IPFS或IPNS地址,而且IPFS还具有与FUSE(用户空间文件系统)的兼容性,这意味着我们甚至可以将整个IPFS挂载为一个只读分区:这一兼容性也让我们能够托管静态网站,但是我必须承认,访问全球(甚至星际)规模的P2P共享盘是个明显更酷的用法。
  407 
  408 *** 在 IPFS 上架设静态网站
  409 [[https://docs-beta.ipfs.io/how-to/command-line-quick-start/][官方指南]]已经很好地概述了使用方法。以下是简要概括:
  410 - 运行=ipfs init=和=ipfs daemon=初始化并启动IPFS守护进程。
  411 - 生成网站文件并运行=ipfs add -r <网站文件路径>=将其内容发送到IPFS。输出的最后几行会有路径根目录的哈希值。
  412 - 如果要使用IPNS,请运行=ipfs name publish <网站根目录哈希>=以将IPNS链接定向到刚刚上传的文件夹上。IPNS公钥的哈希值可以通过=ipfs key list -l=获得。
  413 - 在更新或重建网站文件时重复以上两个步骤。由于IPFS寻址过程固有的数据去重功能,该过程的实际开销并不大。对静态站点这一用例来说非常合适:越大的文件(例如照片)更新频率就越低。
  414 
  415 完成此操作后,我们就可以从任何专用HTTP网关使用=<网关地址>/ipfs/<网站根目录哈希>=或=<网关地址>/ipns/<ipns-地址>=来访问刚才上传的网站了:我们可以使用由IPFS守护进程启动的本地网关(通常位于=127.0.0.1:8080=),也可以使用[[https://ipfs.github.io/public-gateway-checker/][公共网关]](由于IPFS文件取回需要在运行网关的服务器上进行,因此使用公共网关有遭受来自服务器所有者的中间人攻击的额外风险)。如果想要架设多个网站,则可以使用=ipns key gen=来生成更多IPNS密钥对,并在执行=ipfs name publish=时通过=--key=选项指定发布地址。
  416 
  417 在IPFS[[https://github.com/ipfs/go-ipfs/issues/4240][支持IPNS密钥的导入/导出]]之前(这有助于我们备份密钥并从多台设备发布内容),[[https://docs.ipfs.io/guides/concepts/dnslink/][DNSLink]]可用于更方便地访问站点,但代价是需要拥有域名并信任DNS服务提供者。要想通过 =/ipns/<域名>= 从HTTP网关访问站点,只需为域名加入以下TXT记录:
  418 #+begin_example
  419   dnslink=/ipfs/<网站根目录哈希>
  420 #+end_example
  421  422 #+begin_example
  423   dnslink=/ipns/<ipns-地址>
  424 #+end_example
  425 例如本站就可以通过[[https://ipfs.io/ipns/shimmy1996.com/][/ipns/shimmy1996.com]](该链接使用ipfs.io架设的公共网关)来访问。虽然算不上是一个完全没有缺点的办法,但对我来说这是个合理的妥协。我发现IPFS通常比IPNS快,所以在DNSLink里用IPFS地址应该更加合适。为了避免每次手动复制粘贴,我在博客构建脚本中添加了以下内容以自动将网站上传到IPFS并更新DNS记录(使用[[https://developers.digitalocean.com/documentation/v2/#update-a-domain-record][DigitalOcean的API]]):
  426 #+BEGIN_SRC sh
  427   echo "上传网站至IPFS..."
  428   hash=$(/usr/bin/ipfs add -Qr "<网站根目录>")
  429 
  430   echo "更新 DNSLink 记录..."
  431   token="<digitalocean-api-令牌>"
  432   curl -X PUT \
  433        -H "Content-Type: application/json" \
  434        -H "Authorization: Bearer $token" \
  435        -d "{\"data\":\"dnslink=/ipfs/$hash\"}" \
  436        "https://api.digitalocean.com/v2/domains/<域名>/records/<记录-id>"
  437 #+END_SRC
  438 DigitalOcean上DNS记录的记录ID也可以通过[[https://developers.digitalocean.com/documentation/v2/#list-all-domain-records][其API取回]],不过你可能需要在请求中增加=?page=2=或更后面的页码才能找到你想要更新的记录。
  439 
  440 对了,还需要注意的是,正如同使用任何脱机HTML文件时一样,我们需要在生成的网页中使用相对链接。在Hugo中,这可以通过在=config.toml=中加入
  441 #+BEGIN_SRC toml
  442   relativeURLs = true
  443 #+END_SRC
  444 来实现。
  445 
  446 当然,作为一个P2P网络,IPFS无法取回已经不存在于任何节点上的文件。默认情况下,IPFS客户端会[[https://docs.ipfs.io/guides/concepts/pinning/][固定]]从本地计算机共享的任何内容:固定内容不会被删除,这确保IPFS上至少有一个可用的副本。我们可以取消固定网站的过时版本,或者,如果需要,在多台设备上查找并固定网站地址以防万一。
  447 
  448 *** 繁星若尘
  449 回到IndieWeb的问题上:越来越黑的域名系统和链接失效使基于HTTP的URI的稳定性难以保证。但是,如果我们使用IPFS或IPNS地址作为URI呢?简直完美!我们通过由数学而非FBI警告所控制的地址获得了(理论上可以永久持续下去的)对静态网页的稳定分布式访问。消除拥有服务器的需要还降低了拥有个人网站的门槛。HTTP协议已经存在了29年,而IPFS仅存在了5年。我不知道IPFS在接下来的24年中是否还会继续存在,但是如果是的话,我希望我们会看到一个或许更加混乱,但更加健壮、充满活力、多彩的在线世界。
  450 
  451 * DONE TIReD: A Personal Rating System
  452 :PROPERTIES:
  453 :EXPORT_HUGO_SLUG: tired-a-personal-rating-system
  454 :END:
  455 
  456 ** DONE en
  457 CLOSED: [2020-07-17 Fri 21:30]
  458 :PROPERTIES:
  459 :EXPORT_FILE_NAME: 2020-07-17-tired-a-personal-rating-system.en.md
  460 :EXPORT_TITLE: TIReD: A Personal Rating System
  461 :END:
  462 
  463 As the pandemic gives me a chance to look through my backlog of movies, shows, and books (read: anime and manga), I started to consider establishing a personal rating system to ease up writing (hypothetical) reviews.
  464 
  465 *** Guiding Principles
  466 Typical rating scales feature 10 or more levels, which is in my opinion way too wide a range to choose from, not to mention those featuring a 100-point-scales. Even the most common 5-star system gets cumbersome fast as soon as we take half-stars into consideration. What exactly differentiates a 6 from a 7 or a 4.6 from a 5.1? Higher granularity could be useful in aggregated ratings, but not so much from an individual reviewer's perspective. I much prefer the approach [[http://s1vote.com/][s1vote]] took: give the users fewer but more distinctive levels to pick from.
  467 
  468 My anecdotal evidences show that most online ratings converge around the 70% mark, a rating just as safe and useless as [[https://statmodeling.stat.columbia.edu/2020/06/29/this-one-quick-trick-will-allow-you-to-become-a-star-forecaster/][predicting a 40% success rate for anything]]. In other words, the lower half of most rating scales are underutilized: how often would you rate something one-and-a-half-star instead of just one? Besides, more often than not, I read ratings and reviews to find out about good shows, not the bad ones. It should be sufficient to only focus on "the better half": why would I sit through the entirety of a bad show and take the effort to give it a rating anyways? There is no -1 star in Michelin Guide, is there?
  469 
  470 Summarizing the quality of anything with a single metric seems unfair. I want the rating system to be more expressive, capable of conveying the different aspects of a show that I find enjoyable. At the very minimum, an opinionated pick should be distinct from something with a more general appeal.
  471 
  472 *** Rating Methodology
  473 Enter the TIReD scale! The following uses anime/tv shows as the example here, but much of this methodology also applies to other art forms. A show is scored in the following categories, with sum of points forming the final rating:
  474 
  475 | Category                         | Range |
  476 |----------------------------------+-------|
  477 | <strong>T</strong>angible        |   0-2 |
  478 | <strong>I</strong>ntangible      |   0-2 |
  479 | <strong>Re</strong>visit-ability |   0-1 |
  480 | <strong>D</strong>iscretionary   |   0-1 |
  481 
  482 Tangible aspects of a show include visual style, animation, soundtrack, CG quality, special effect, etc. To put it simply, how physically well-made a show is. Starting from a score of 0, a show would be scored a
  483 - +1 if the show is overall attractive to watch and either has consistent high quality with very few shortcoming (perfection) or utilizes unique ideas/techniques to great effects (ingenious);
  484 - +2 if its physical quality/way of expression alone would be sufficient reason to watch the show, even if it gets a 0 in all other categories.
  485 
  486 Intangible aspects include story, character building, plot pacing, cultural reference, etc. This quality should be relatively medium independent, i.e. I would enjoy a faithful recreation of the story in other art forms at least just as much. Criteria for scoring is similar except for remakes/adaptations with an clear intent to follow the original and when I have seen/read the source material: scoring would be based on the source material's intangible score adjusted downwards by 1 point, with at most extra 1 point adjustment based on quality/difficulty/effect of the remake/adaptation with in the range of 0-2. For instance, a mediocre retelling of a +2 story should only be awarded at most a +1. Remakes and adaptations probably have an easier starting point than original contents, so I wanted to adjust for "how good the show could have been", provide an answer to "should I still see this if I've seen the original", and pick out the "watch this instead of the original" or "transcended and elevated the original story" shows.
  487 
  488 Revisit-ability, as the name indicates, represents whether I would want to revisit/rewatch the show later. This correlates more with my own taste or nostalgia: is this something that I would gladly jump into in an leisure afternoon. Longer shows tend to suffer a bit by this metric, so I would take into account of especially memorable segments/episodes. However, in event of remakes and adaptations, this point should generally only be rewarded to the best version of the work in my point of view.
  489 
  490 Discretionary point should be awarded sparingly and only when a show doesn't already achieve full scores in all other categories, making the possible maximum score 5 instead of 6. This is used as an adjustment for shows that I feel the current rating system doesn't do it justice. Common situations where this applies include but are not limited to:
  491 - categorical superiority: best of its kind;
  492 - a tight coupling between tangible and intangible aspects of the work: it simply won't be the same without one another;
  493 - quality in spite of objective limitations, especially for older shows or those with a tight budget.
  494 
  495 *** Format
  496 A TIReD rating is recorded as =X=T/I/Re[+D]=. For instance:
  497 - a show scoring 1 in tangible, 2 in intangible, 0 in revisit-ability, and 0 in discretionary would be recorded as =3=1/2/0=;
  498 - a show scoring 1 in tangible, 0 in intangible, 0 in revisit-ability, and 1 in discretionary would be recorded as =2=1/0/0+1=.
  499 
  500 Shows that I abandoned halfway, meaning I won't be able to give a rating, will be marked as =DNF= (did not finish).
  501 
  502 *** Self Q&A
  503 Some fragments of thoughts that I came across when designing TIReD.
  504 
  505 Q: How should tangible points for books be awarded?
  506 
  507 A: I'd say it's how good the writing is at face value, i.e. is it "literature" worthy. While I not really confident in my ability of identifying great works, but a +2 should at least be something better than /Harry Potter/.
  508 
  509 Q: How should world settings built up in previous/related works affect the rating?
  510 
  511 A: World building actually fits into both revisit-ability (if the system/world is interesting and makes me want to read more about it) and intangible quality (whether the character actions are justified).
  512 
  513 Q: How was the rule for discretionary point determined?
  514 
  515 A: The best shows should always get full score regardless of the exact scale, so awarding them discretionary points is meaningless. However, there are seemingly not-so-impressive works that really show the passion/devotion/love/good faith of the production team/author and shows whose existence alone is a boon for its fans. I want to express my enjoyment in a way that still allows me to assess the tangible and intangible aspects of a show on an absolute scale, as any further complication can be taken account of as discretionary point.
  516 
  517 Q: What happens to ratings for a remake before and after you watch the original?
  518 
  519 A: I'll adjust score for the remake now that I have experienced the original.
  520 
  521 Q: A lot of details could be lost in translation. How to deal with translated works?
  522 
  523 A: For now I will treat these the same way as remakes: adjust the rating if someday I came across the original.
  524 
  525 Q: How did you come up with the name "TIReD" (and name for the categories)?
  526 
  527 A: The first category to have a concrete name is revisit-ability. From there on it's mostly just playing around with words and initials. I almost settled on "TIRD" thanks to [[https://www.urbandictionary.com/define.php?term=tird][Urban Dictionary]]. Well, not everything is sh*t. 😜
  528 
  529 ** DONE zh
  530 CLOSED: [2020-07-17 Fri 21:30]
  531 :PROPERTIES:
  532 :EXPORT_FILE_NAME: 2020-07-17-tired-a-personal-rating-system.zh.md
  533 :EXPORT_TITLE: TIReD:我的评分系统
  534 :END:
  535 
  536 疫情给了我更多时间来回顾想看的电影、节目和书籍,所以我考虑建立一套自己的评分系统,以简化写(如果有的话)评论的过程。
  537 
  538 *** 指导原则
  539 一般的评分标准具有10个或更多级别。这个选择范围在我看来太大了,更不用说那些采用百分制的评分系统了。即使是最常见的五颗星系统也会在引入半颗星后变得繁琐。6分与7分或4.6分与5.1分间究竟有什么区别?较高的精细度在汇总大量评分中可能会有用,但从单个评论者的角度却不是这样。我更喜欢[[http://s1vote.com/][S1漫区投票]]所采用的方法:让评论者在更少但区别更明显的几个级别中作出选择。
  540 
  541 我的身边统计学表明,大多数在线评分都聚集在70%周围,这是个与[[https://statmodeling.stat.columbia.edu/2020/06/29/this-one-quick-trick][预测任何事物的成功率都为40%]]一样没用的分数。换句话说,大多数评分系统的下半部分都没有得到充分利用:我想不到任何我会给出一星半而非一星评价的情况。此外,我查看评分和评论的目的在于了解高质量的作品,而不是差的那些。一个评分系统仅关注“更好的那一半”就足够了:为什么我在忍受完一个糟糕的节目后,还要给它一个评分?《米其林指南》可没有负一颗星,不是吗?
  542 
  543 单纯地用一个分数来概括任何东西的质量都不怎么公平。我想有一个更具表现力的评分系统,以传达我对所喜欢作品不同方面的看法。至少一个恰好对上我的波长的作品应该和一个更具大众吸引力的作品得到不一样的评价。
  544 
  545 *** 评分方法
  546 隆重推出TIReD评分系统!以下主要以动画/电视节目为例,但这种方法中的许多内容也适用于其他艺术形式。每个作品会在以下项目中进行评分,分数之和构成最终评分:
  547 
  548 | 项目                                       | 范围 |
  549 |--------------------------------------------+------|
  550 | 有形(<strong>T</strong>angible)          |  0-2 |
  551 | 无形(<strong>I</strong>ntangible)        |  0-2 |
  552 | 回味(<strong>Re</strong>visit-ability)   |  0-1 |
  553 | 酌情加分(<strong>D</strong>iscretionary) |  0-1 |
  554 
  555 作品的有形方面包括视觉风格、动画、配乐、CG质量、特殊效果等等。简单来说,作品的制作是否精良。以0分为起点,评分标准为:
  556 - 1分:如果作品总体上吸引人,且维持了稳定的高质量、缺点极少(完美)或利用独特的想法、技术产生了非常棒的效果(高妙);
  557 - 2分:作品采取的表现手法本身就足以作为观看的理由,即使作品在其他项目中得分全部为0分。
  558 
  559 作品的无形方面包括故事本身、角色塑造、剧情节奏、文化背景参考等等。这些特质应该一定程度上独立于作品的具体表现介质而存在:我至少可以同样地享受基于其他艺术形式对作品的忠实再现。评分标准与之前类似,不过对于明显遵循原作内容的翻拍或改编作品,且我看过原作时,评分标准会有所调整:评分将以原作在无形方面的得分-1为起点,视翻拍、改编的质量、难度、效果作出最多1分的调整后舍去超出0-2分的部分。例如,对+2故事的平庸重述最多只能授予+1。翻拍和改编的起点往往比原创容易一些,所以我想以“作品本来可以有多好”为基础作出调整,对“如果我看过原作,还应该看吗”这类问题作出回应,并方便挑选出那些“不必看原作”或“已经超越了原作”的作品。
  560 
  561 回味,顾名思义,衡量我是否想重温这一作品。这更多地与我自己的口味或怀旧之情相关:这应该是我会在一个无所事事的午后重新观看的作品。较长的作品在这一指标上会处于比较不利的地位,所以我会将特别令人难忘的片段也计算在内。在翻拍和改编的情况下,这一项目的得分大多数时候都应该仅授与我认为最好的版本。
  562 
  563 酌情加分不应该轻易使用,且对已经在其他三个项目中获得满分的作品无效,这使得最高总得分为5分而不是6分。设置这一项目的主要目的是对于我认为作品优点没能很好地被现有评分标准体现出来时作出调整。适用的常见情况包括但不限于:
  564 - 类型天花板:同类最佳;
  565 - 作品的有形和无形方面有着紧密的联系,缺一不可;
  566 - 超越客观条件限制的质量,尤其是对于年代久远或预算紧张的作品。
  567 
  568 *** 记录格式
  569 TIReD评分记录的格式为=X=T/I/Re[+D]=。例如:
  570 - 一个有形得分为1分、无形得分为2分、回味得分为0分、酌情加分为0分的作品会被记录为=3=1/2/0=;
  571 - 一个有形得分为1分、无形得分为0分、回味得分为0分、酌情加分为1分的作品会被记录为=2=1/0/0+1=。
  572 
  573 对于我半途放弃看下去的作品(意味着我无法给出评分),会被标记为=DNF=(did not finish)。
  574 
  575 *** 自我问答
  576 我在制定TIReD时的一些想法碎片。
  577 
  578 问:应该评定书籍的有形得分?
  579 
  580 答:我觉得应该就是看行文本身的质量,例如其能否称为“文学作品”。虽然我对自己鉴定优秀作品的能力并不那么有信心,但是至少获得2分的作品应当比《哈利波特》要好。
  581 
  582 问:在其他相关作品中建立的世界设定如何影响评分?
  583 
  584 答:世界设定会影响回味得分(系统或世界是否有趣、让我想进一步了解)和无形得分(人物背景是否合乎其行为)。
  585 
  586 问:酌情加分的规则是如何确定的?
  587 
  588 答:不论分数范围多大,最好的作品始终会获得满分,所以给它们再加分没有什么意义。另一方面,有一些看似不起眼的作品却充满了制作团队、作者的热爱和诚意,还有一些作品存在本身就足以称为爱好者的福音。我想在保证较为客观地评判作品的有形和无形方面得分的前提下表达自己的欣赏,而酌情加分提供了一个途径。
  589 
  590 问:在看原作前后,改编或翻拍作品的评分会如何变化?
  591 
  592 答:在看过原作后,我将调整改编或翻拍作品的分数。
  593 
  594 问:翻译常常会漏掉一些细节,如何处理翻译作品?
  595 
  596 答:处理方式与翻拍相同,在看过原作后评分会被调整。
  597 
  598 问:“TIReD”(以及分项得分名称)是怎么确定的?
  599 
  600 答:第一个有具体名称的项目是回味(revisit-ability),后面的大部分时间都花在玩单词排列和缩写上。由于[[https://www.urbandictionary.com/define.php?term=tird][Urban Dictionary]],我差点将这套评分标准命名为“TIRD”。不过,至少不是所有的作品都是*。😜
  601 * DONE Get GOing
  602 :PROPERTIES:
  603 :EXPORT_DATE: 2020-12-25
  604 :EXPORT_HUGO_SLUG: get-going
  605 :END:
  606 
  607 ** DONE en
  608 CLOSED: [2020-12-25 Fri 18:50]
  609 :PROPERTIES:
  610 :EXPORT_FILE_NAME: 2020-12-25-get-going.en.md
  611 :EXPORT_TITLE: Get GOing
  612 :END:
  613 
  614 Yes, I [[https://git.shimmy1996.com/advent-of-code/][finished]] the [[https://adventofcode.com/][Advent of Code]] this year! Aside from the problems being easier (for me) than 2019, I'm also using Go for this year's challenge and I find it to be particularly suited for this type of endeavor.
  615 
  616 This year's puzzles mostly involve string parsing and finding efficient data structures. Majority of the logic flow are pretty straight forward and there's little need for sophisticated algorithms.
  617 
  618 For string parsing, regex, which Go has built-in support for, is definitely the way to go. The abundance of parsing related problems means using only basic string manipulation could be rather painful, and I've definitely seen my share of horrible blobs of find/substr/trim.
  619 
  620 Most of the time, slices and maps are all I needed. Go has multiple return values but no tuples, whose usage, I find, is largely replaced by either arrays or structs. Versatility of these data structures are actually increased due to the language's encouragement to use constants instead of enums: storing all information as ints opens up the door to some shortcuts and less conversion between types. Surely they don't give you the peace of mind type checked enums provide, but (ab)using them in short programs does provide the odd walking-on-a-knife-edge (or not-wearing-pants-during-Zoom-call) kind of satisfaction.
  621 
  622 Imperative programs are easy to write in Go, mostly because of the language's plain and simple control flows and lack of mixed paradigms. There's no need to worry about whether we should use an STL algorithm or chained iterator methods: just write the loop. Reasonable mutability behaviors also helps: whether it's changing a map while looping through it or passing a struct containing a slice to another function, I can get the language to do what I mean without checking the specification line by line.
  623 
  624 There's [[https://en.wikipedia.org/wiki/Zero_one_infinity_rule][the ZOI rule]] about how the only reasonable numbers are zero, one, and infinity. Quite a few other languages I know, such as Python, C++, and Rust, all seem to hinge on the extreme ends of the spectrum in pursuit of consistency: everything follows the same rules and users can dictate what the syntax means as much as the base language. Go definitely has more exceptions (without supporting it) and "one" moments: built-in containers are magically generic, their methods can have variable number of return values, and everything else is denied the privilege of being eligible to be iterated over.
  625 
  626 While just a quick comparison without touching other traits of Go (say interfaces or goroutines, but you don't really need them for Advent of Code), I do find Go's choices peculiar and interesting: [[https://lukeplant.me.uk/blog/posts/everything-is-an-x-pattern/][everything is, well, just its own thing]].
  627 
  628 ** DONE zh
  629 CLOSED: [2020-12-25 Fri 18:50]
  630 :PROPERTIES:
  631 :EXPORT_FILE_NAME: 2020-12-25-get-going.zh.md
  632 :EXPORT_TITLE: Get GOing
  633 :END:
  634 
  635 我今年[[https://git.shimmy1996.com/advent-of-code/][完成]]了[[https://adventofcode.com/][Advent of Code]]!除了问题比去年(对我来说)变得简单了之外,我换用Go完成了今年的挑战:我觉得Go特别适合这类任务。
  636 
  637 今年的谜题主要涉及字符串解析以及寻找最高效的数据结构。大部分解题流程的逻辑都比较简单,几乎不需要任何复杂的算法。
  638 
  639 对于字符串解析,正则表达式(Go自带支持)是最佳途径。今年为数不少的字符串解析问题意味着想只用基本的字符串操作解答会十分痛苦。我已经看够了用查找、剪切、提取子串等操作拼砌起来的怪物代码。
  640 
  641 绝大部分时候,切片(slice)和集合(map)足够满足我的要求。Go支持多返回值,却不支持元组(tuple);不过,我发现序列(array)和结构体(struct)基本取代了元组的功能。这些数据结构由于Go鼓励使用常量而非枚举类型(enum)而变得更加多功能:将所有信息储存为整形(int)使得一些捷径成为可能,同时也省去了类型转换的麻烦。和更让人安心的支持类型检查的枚举类型不同,(滥用)这些选择让用Go写短程序有着和行走在刀刃上(或者在Zoom通话时不穿裤子)一样的莫名快感。
  642 
  643 Go简单明了的控制流和缺少多范式支持的特点让指令式程序编写起来非常容易。不必担心是否应该使用STL算法或者串联迭代器方法:写个循环就好了。合理的对象可变机制也有帮助:不论是边遍历边修改集合元素,还是将带有切片的结构体传给函数,我发现我可以很快地让Go做我所想的事情,而不用反复翻阅语言规范。
  644 
  645 这让我想到了[[https://en.wikipedia.org/wiki/Zero%5Fone%5Finfinity%5Frule][ZOI这一经验法则]]:只有零、一、无穷多是合理的个数。不少我所熟悉的编程语言,例如Python、C++、Rust,都为了追求一致性落在这一光谱的两极:一切都有着相同的规则,用户就和语言本身一样有着决定语法含义的能力。Go有着更多的异常之处(尽管Go并没有异常支持)和只有“一”个的例外:内置容器神奇地有泛型支持、它们方法的返回值数量可变、除它们之外的容器都没有获得迭代支持的资格。
  646 
  647 这只是一个不涉及Go其他特性(例如界面(interface)或协程(goroutine),不过这些对解决Advent of Code并不是必须的)的简单比较。我觉得Go的选择独特且有趣:[[https://lukeplant.me.uk/blog/posts/everything-is-an-x-pattern/][一切都,嗯,不大一样]]。