回顾 2018

新年快乐!

一年时间的相对长度比起一个人已经度过的时间总是在不停地缩短。如果一年的长度也随着人的年龄增长的话,我们在新年倒计时中的感受大概会有更多的兴奋而非焦虑和不安吧。话说回来,就算没有光追显卡, 2018 年对我来说也很有趣。

The Amazing 2018

引用 2017 年的自己:

如果我有从过去那些我制定后没能执行的计划里得到任何经验的话,那就是定计划时最好稍微低估自己的能力...

是的,从我 2018 年目标的完成状态可以看出,一定是对空闲时间的估计上出了问题。嗯,一定是这样。

  • ☒ 跑 1000 英里。[405/1000]
  • ☒ 完成一次马拉松。
  • ☒ 写 20 篇以上的日志。[10/20]
  • ☒ 获得第一个 PGP 密匙签名。
  • ☒ 安装 Gentoo。

由于使用 hugo ,我可以随时更改博客条目的“发布日期”,我养成了撰写文章开头后将其搁置好几个月的坏习惯。当我最后记得那篇未完成的文章时,我经常认为这个想法不值得详细写下去。现在想想,也许这正是博客的目的,它提供了我在某个时间点的快照,使得我可以回顾过去的自己,无论将来的我会觉得这是愚蠢的还是“不值得详细写下去”的。

我在 2018 年的电影院访问次数可能占我一生的总数的 50 %,但平均失望程度却因为 星球大战:最后的绝地武士超人总动员 2 而翻倍。 顺便一提,2018 年的爆米花消费量也达到了我一生总量的 90 %。我从来没有意识到爆米花会如此令人上瘾。

尽管不是全程马拉松,但我在 5 月参加了第一次山地半程马拉松比赛。这是我第一次在跑步时感到体力不支,原因是配速不佳以及对天气的准备不足。比赛当天极其炎热,且比赛从下午开始。在目睹许多人在头 2 英里内停下来走路后,由于愚蠢的虚荣心作祟,我的初始速度比我预期的速度要快得多,并在 4 英里的时候不得不因为体力不支停下。所幸,在我走完接下来的一半赛程并在每个补给点大量补充佳得乐后,疲劳的感觉消失了。但是,我没想到那些佳得乐会是另一个陷阱,太阳落山的同时带着气温迅速下降,我的胃开始因为摄入太多冰冷的液体而开始抽搐。当终点线出现在距我视线 400 米以内的地方时,我的双腿受到了我有史以来最强烈的抽筋的打击。在距离终点一步之遥的地方连续被 3 个人超越之后,我终于勉强完成了比赛,但我很高兴得知自己还不是最后一名:实际上,我甚至是我这个( 大概只有一个人完赛的 )年龄组中的第一名。来的名不正言不顺的第一名带来的喜悦,混杂着一点点的不甘心和罪恶感,使那场比赛成为了一次奇妙的经历。

The Spectacular 2019

由于 Google 即将在 3 月淘汰 Inbox ,我失去了继续使用 Gmail 的最后借口。 我将逐渐淡化 Gmail 的使用,转向我自己的电子邮件服务器。

说到博客评论的最佳解决方案,我关注的许多博客作者已经开始采用 IndieWebWebmention 。在很多方面, Webmention 正是我想要的东西:它提供了分布式的博客评论、日志等等。 但是,我不愿舍弃静态站点的好处,更不用说我发现大多数易于遵循的 Webmention 解决方案都严重依赖第三方服务。 IndieWeb 运动倒是很吸引人。说起来我的 Keybase 除了作为一个联系我不同线上身份的枢纽外并没有太大用处(在线解密和加密功能需要上传 PGP 私钥才能使用,安全消息功能对与并没有什么人可以聊天的我更是派不上用场),也许我应该用 rel=me 链接来完全取代它。

去年学习 C++17 的体验非常令人享受,因此我正在考虑学习其他新的编程语言。我已经窥觎 RustJulia 一段时间了,尤其是 Rust 。拥有一整套官方支持的工具链使写 Rust 变得顺畅而愉快。我会尝试深入了解并实际使用这两种语言。

至于跑步和博客日志,我将尝试维持 2018 年的数字。除此之外,我考虑在博客上记录看_听_读过的书籍,音乐和节目,以及自己的想法。过去在本站的 Wordpress 时代,我有过类似的尝试:我搭建了一个 MediaWiki 实例来记录这些,但缺乏继续维护条目的动力。这次我会用一些更轻量的解决办法,并且构思一套评分系统。

我应该如何处理其余的 2018 年目标呢?单独的愿望清单是一个很好的主意。作为一个额外目标,我应该清理一下那台堆积了四年份灰尘、猫毛和死皮细胞的台式机。

为接下来的 2.9e+17 个铯 133 辐射周期干杯!

安装 Gentoo

我终于下定决心在 VirtualBox 上尝试了安装 Gentoo(绝对不是因为首页的愿望清单),从而实现了我的终极数字飞升(按照 DistroWatch 排名的话应该是下凡)。

话说回来,安装过程十分顺畅: Gentoo 手册 编写的很出色,似乎预料到了所有可能出错的地方并准备好了后备反感。与 ArchWiki 的 安装指南 相比,我更喜欢该手册,因为手册还详细介绍了我采取的每一步背后的原因。我甚至觉得,Gentoo 手册实际上是对初学者更友好的,因为它精心汇总了了通常散布在各处的信息,为学习如何驯服你的操作系统提供了一个很好的起点。 此外, Gentoo 手册不仅涉及安装,还包含其他设置一个可用的系统的必要步骤。我将逐步复制我当前的台式机设置,以决定是否值得进行迁移。

我第一次接触 GNU/Linux 操作系统是 Ubuntu 12.04 :我的一位同学( vacuuny/A2Clef )在学校的计算机实验室中安装了它。曾经有一段时间我每隔几天会在各种 Ubuntu 版本之间进行切换。在同时使用 Windows 和 Ubuntu 一段时间后,我在 2014 年完全切换到 Ubuntu 。由于 Ubuntu 上亚马逊广告的猖獗,我尝试了 Arch Linux ,作为 2015 年新年计划的一部分。即使有第二台计算机来查找说明,我也花了相当长的时间来适应新系统。我在旧博客中还曾写到“大概我还没有 get 到 the Arch way ”。但是完全熟悉 Arch Linux 后,我就再也没有回头。

我仍然会不时在 VirtualBox 中尝试其他发行版,但是除了设置过程之外,我从未发现它们与 Arch 相比能够提供多少改进,更不用提 ArchWiki 上极为出色的文档(现在我们有一个竞争者了)。设置好桌面环境后,发行版之间的体验并没有太大区别,但是当我遇到问题并在线搜索如何故障排除时,区别就开始出现了。拥有更多、更新的软件包是 Arch 的另一项魅力。最近,关于 systemd 的争议使我开始四处寻找新发行版以进行试用。与其说是因为实际的安全问题,不如说我只是想试试使用不同的初始化系统:在 Ubuntu 下我主要使用图形界面( apt-getnano 可能是我很长一段时间里知道的唯二命令)所以并没有什么直观感受,而在我换用 Arch 时, Arch 已经在使用 systemd 了。除了 Gentoo ,候选对象还包括 Void Linux 和 BSD 。 Void Linux 有易于使用的安装向导,但我并不感到它有特别吸引我的地方。看看 Gentoo 是否会改变我的想法。

触摸板和膨胀的电池

在过去的几周中,我的 Dell XPS 13 触控板右键变得越来越难以使用:整个触控板的右半部分沉入了掌托下方约 2mm 的位置,使右击难以被记录。最初我认为是正常磨损,但事实上是膨胀的电池将触控板的左半部分拱起,导致触摸板变形。 我立即订购了 OEM 部件( Dell JD25G ),更换了膨胀的电池。XPS 13 ( 9343 )还算易于维修。固定底板(相当大的一块铝块)的螺钉都清晰可见,并且组件布局允许在打开底板后直接更换电池。 我还将无线网卡( Dell DW1560 )换成了 Intel AC9560,其驱动程序在主线 Linux 内核中,方便不少。

更换电池后,触控板恢复了正常。但是,笔记本电脑电池平均在 18 个月左右开始性能下降这一事实仍使我感到非常惊讶。 我这块持续了近四年的电池已经算不错了。较新的笔记本电脑大多使用方形电芯(它们也被用在智能手机中的平板状电池里),而非我的第一台笔记本电脑 Dell Vostro 3750 中搭载那种的圆柱形电芯。电池膨胀一般是由气体积聚引起的,这在带有通风孔的圆柱形电芯中可以避免。有趣的是,可拆卸电池在消费类笔记本电脑中已基本消失 - 即使是大型的台式机替代品(虽然这些笔记本电脑大部分时间都插在电源上)。我能想到的唯一仍然几乎总是具有可拆卸电池的消费电子产品是相机。

这一事件之后,我开始浏览当前市面上的笔记本电脑,因为带有新的四、六核心 CPU 的笔记本电脑是极具诱惑力的升级(我的 XPS 13 配置了 i5-5200U )。我不怎么喜欢最新版本的 XPS 13(9380),主要是因为端口选择:我目前没有任何 USB Type-C 设备,因此我认为 XPS 13 (9360)上的一个 Type-C 加两个 Type-A 的组合更加优越。除了端口之外,板载无线网卡和全尺寸 SD 卡插槽的移除也使最新型号的吸引力降低。

我还查看了 Panasonic 的 Let's Note 系列笔记本电脑。这些笔记本电脑是可靠而轻便的商务笔记本电脑,并通常配备可拆卸电池和各种端口。如果要是它们没有那么夸张的价格、没有那些丑陋的“ Wheel Pad ”、并配备美式键盘布局,那它们就是理想的笔记本电脑。我最喜欢 2016 年推出的 CF-MX5 系列的外观,但这一系列的性能比起我目前的配置并不会有多大提升。

更为现实的选择包括惠普的 EliteBook ,联想的 ThinkPad T 系列和戴尔的 Latitude 、 Precision 系列。 我否决了 EliteBook ,因为系列所有机器上都有一个巨大的、我可能永远不会使用的专用坞站端口。由于采用了设计功耗 45W 的 CPU, Latitude 5491 似乎有散热问题,但 Latitude 7390 和 7490 看起来都不错,不仅可以禁用 Intel ME 还带有官方 Linux 支持。 ThinkPad T480 几乎满足了我的所有要求,但下一次代的 T490 似乎将不再具有桥接电池系统并仅保留一个 SODIMM 插槽,与 T480s 差不多。

寻找二手机器也是一种选择,但是由于我的主要动机是购买新的四核 CPU ,所以这达不到升级的目的。 有的人认为我们的笔记本电脑的处理性能早已超过我们的日常需求,况且我的 XPS 13 使用时确实感觉不慢,因此我并不急需进行升级。不过我还是列了一下我对理想中笔记本电脑的需求,以备万一。

  • 良好的 Linux 驱动程序支持。
  • 尺寸小于 15 英寸,旅行重量轻。 XPS 13 将我从 DTR 爱好者转变为 Ultrabook 追随者:能够整天携带笔记本电脑而几乎感觉不到重量非常棒。
  • 非 Nvidia 显卡。 AMD 和 Intel 都具有更好的开源驱动程序支持,而且高度依赖 GPU 的任务还有台式机可以分担。
  • 合理的电池寿命( 6 小时或更长时间)和可拆卸电池。
  • 不过于激进的接口选择,至少直到所有鼠标和闪存驱动器默认接口都为 USB C 型的那一天。
  • 使用标准组件,易于升级,例如内存使用 SODIMM 插槽、无线网卡和硬盘使用 PCIe 等等。
  • 不错的触控板。我对笔记本电脑键盘的质量不太敏感,任何质量尚可的键盘我都能接受。不过要是有搭载 ErgoDox 的笔记本电脑就好了。
  • 非超高分辨率的显示屏。我对屏幕也不是很挑剔,但是对于这种大小的笔记本电脑来说,采用 4K 分辨率完全是高射炮打蚊子。我通常使用 16:9 比例的屏幕,但不反对尝试其他分辨率比例。

我最近开始使用安全密钥作为除一次性密码之外的双重认证手段。 然而让我感到沮丧的是,许多网站只在 Chrome 下完整支持安全密钥,比如 Google (注册新密钥必须使用 Chrome ,但 Firefox 正在修复这一问题),和 Github (“我们建议更新到最新的 Google Chrome 以开始使用安全密钥设备”)。考虑到微软最近的 Edge 和 Skype 更新,不难猜到幕后黑手是谁。

今天我的密匙又可以在 Github 下使用了(在 Firefox 里)。希望昨天的情况只是偶发事件。

网页版 Google Inbox 看起来还活着! 我已经不在手机上使用 Inbox 了:作为替代,我在 Gmail 里设置了邮件转发以便使用 iOS 自带的邮箱程序。 但是,我仍然更喜欢网页版 Inbox 多过网页版 Gmail。 让我们看看 Inbox 还能撑多久。

Inbox 从这个世界上消失了。:(

用 C++ 来 enumerate()

不少编程语言都提供了在迭代容器的同时记录步数的方法,例如 Python 的 enumerate()

for i, elem in enumerate(v):
    print(i, elem)

以及 Rust 里 std::iter::Iterator 特性下的 enumerate()

for (i, elem) in v.iter().enumerate() {
    println!("{}, {}", i, elem);
}

这里记录了如何在 C++17 或更新的标准里尽量简洁地实现类似功能的办法。

第一种方法是使用一个可变的 lambda :

std::for_each(v.begin(), v.end(),
              [i = 0](auto elem) mutable {
                  std::cout << i << ", " << elem << std::endl;
                  ++i;
              });

这个方法使用于所有能够保证 lambda 有序执行的算法,但是我并不喜欢末尾很可能被混入其他逻辑的 ++i

第二种方法是在 for 循环中使用结构化绑定:

for (auto [i, elem_it] = std::tuple{0, v.begin()}; elem_it != v.end();
     ++i, ++elem_it) {
    std::cout << i << ", " << *elem_it << std::endl;
}

为了不让编译器默认创建只允许同种内容的 std::initializer_list ,我们必须加上 std::tuple

第三种最朴实无华的办法是在循环的每一步计算指针距离:

for (auto elem_it = v.begin(); elem_it != v.end(); ++elem_it) {
    auto i = std::distance(v.begin(), elem_it);
    std::cout << i << ", " << *elem_it << std::endl;
}

由于这种方法需要我们在两个地方指定初始指针,我更喜欢之前提到的基于计数器的方法。

在 C++20 中,我们可以在基于范围的 for 循环中加入初始化语句:

for (auto i = 0; auto elem : v) {
    std::cout << i << ", " << elem << std::endl;
    i++;
}

新加入的 <ranges> 库则提供了一种更加吸引人的实现方法:

for (auto [i, elem] : v | std::views::transform(
         [i = 0](auto elem) mutable { return std::tuple{i++, elem}; })) {
    std::cout << i << ", " << elem << std::endl;
}

我最喜欢基于结构化绑定和 <ranges> 库的方法。当然如果要是有 std::views::enumerate 来一劳永逸地解决这个问题就最好不过了。