Chaofan

For the next train

作者: qcf

  • 效率软件杂谈

    说起来自己几年来用过的效率类 App 也不少了,尽管自己很难坚持,不过倒是有个喜欢折腾的习惯,从早年的 Linux 到现在的效率软件。正好好久没更新博客了,就随便对它们说说自己的看法。

    效率类 App 的外延很大,从代码管理到任务规划,只要是能够提高工作、学习效率的软件,都可以划入效率这类范畴。不过,我们日常看到的多数效率软件无非都是写作、协作和日历事项。我想一方面是因为它们的开发门槛较低,不需要什么额外的领域知识(想想前端框架都用 TODO-List 做 Demo,搞个能用的并不麻烦);另外一方面,大众用户都需要这类软件,除了我等喜欢倒腾比较它们的爱好者之外,还有大量的用户此前从未接触过(仅因为一些偶然发现的小功能就感叹生活质量提高了),他们也完全是潜在市场。当然,用过一些雕琢不够细致的作品也会发现,要把它们做好,也不是那么容易的。

    奇妙清单

    奇妙清单是相当老的 TODO 列表软件了,对普通用户几乎免费。早期对一些要求团队协作的高级功能收费,被微软收购以后就停止高级功能和收费,但也不再更新了。奇妙清单的一大亮点在于全平台支持,从 iOS 到 Windows 乃至 Windows Phone 通通有 App,并且桌面端也是原生,实在不容易。(其实让几个 iOS 程序员兼任开发 Mac App 的任务也不会很难,满大街的 Electron 和 nw.js 实在影响体验)

    Wunderlist Sample

    如果之前没有过使用这类 App 习惯的话,奇妙清单在一开始可以有效地帮助自己捋清今天要做的事情。但它的功能实在有些单薄,不明显的子任务入口,缺乏记录和统计功能,实在让它只能安心做一个清单应用。无独有偶,最近兴起的一系列 App,从 Bear 到 Agenda,都将任务列表和笔记功能糅合在一起。尽管在提醒上有些欠缺,不过每填工作上都要主动查阅修改的记录,实在不需要严格的提醒。需要的,交给自带的提醒事项就好了。后面要提到的 Emacs 中著名的 org-mode 也基于这个逻辑。尽管它也可以用来记笔记、写文档,但如果一点都不用上它乍看奇怪却又灵活强大的 TODO 功能,总觉得会缺了些什么。

    当然了,任何软件都只是辅助提高工作生活效率的工具,改变自己的工作、学习节奏也一定是一个系统化的、长期的过程。寄改变生活方式的希望于某个软件并不现实。奇妙清单的功能并不复杂,容易上手。如果你此前没有太多折腾数字化工具的经验,可以将它作为入口,管理生活并进一步探索适合自己的方法体系。即使日后你认为它管理任务功能不足,至少作为购物和杂事清单还是不错的。一直都有传言称奇妙清单要倒闭或者停止运营,不过它大概还会在目前的状态下运营较长一段时间。即使要关闭服务器了,因为它提供了完善的 API,所以在发布通知之后,你也有足够的时间一键将已完成和未完成的任务列表转移到其他应用(比如 Todoist)中。当然,微软收购团队之后推出的 Microsoft To-Do (目前尚无 Mac 版)也是一个不错的相似选择。

    Ulysses

    某种程度而言 Ulysses 不算本文想描述的效率软件,不过实在有很多人推荐过它(包括后面的 iA Writer),所以也值得谈一谈自己对它的看法。Ulysses 是一个有相对久远历史的文本编辑器(好吧,有很多人习惯称呼它写作软件),提供了编辑 Markdown XL(一个扩展的 Markdown 语法)文本和管理文档等一系列功能。如果谈及 Mac 上辅助写作的软件,一定会有人提到 Ulysses,足以说明它在诸多 Mac 用户心中的地位。当然,Ulysses 也有 iOS 版本(尽管第一代 Ulysses 发布的时候好像还没有 iPhone),并提供基于 iCloud 的同步。

    Sample of Ulysses (From Web)

    我不是 Ulysses 的深度用户(使用 Ulysses 写的字数应该不超过 10000),体验它也主要是因为 SetApp 提供的试用机会。不过它的可定制主题、可定制 Markdown 语法、打字机模式、流畅的写作体验和文档管理等确实令人印象深刻。打开一个全屏界面很容易让人产生写点什么的冲动。(尽管用键盘而不是笔写作长文还是一个需要适应的过程)iOS 版界面相对简单,不过也足以在手机上进行临时的写作工作了。Ulysses 的同步没有使用 iCloud Drive,也就是说保存在文档库中的文档并不是对用户直接可见的(尽管你还是可以在 Mac 上的 Mobile Documents 目录中找到它),这点和 iA Writer 不同,同步也有稍长的延迟。

    用 Ulysses 来书写内容当然是足够的,然而写作本身必然也具备痛苦和苦闷,正如这个软件的名字,尤利西斯。值得一说的是它的价格。曾经的 Ulysses 在 Mac App Store 定价 45$(中国区 298¥ 左右),iOS 版定价 25$(中国区 128¥ 左右),算是普通用户需要的软件里面价格相对高昂的了。(我不禁想起当年花了快 400 大洋入的 Photoshop CS6 教育授权,结果一直吃灰)这个价格见仁见智,对很多人来说就是几顿饭的钱。但是你可能也会觉得,它的功能可替代性太强,几乎能写代码的编辑器都支持 Markdown,Mac 上还有好些开源编辑器——我为什么要用它?另外,我也看到过用户反映,Ulysses 在对较长(数千字)文章进行编辑时会有明显卡顿感。这也算是针对写作的文本编辑器的一个通病。(诸如 VS code、Sublime Text、Vim、Emacs 这样的代码编辑软件中倒是很少看到这种现象)

    不过,随着苹果企图在 App Store 推进订阅制,提高开发者从订阅中得到的分成,越来越多的软件采用订阅制,Ulysses 是其中引人注目的一员。对很多大型软件来说,订阅制是一个相对划算的更新方案(相比于每次出新版本之后再买断),比如 Office 365 和 Adobe Creative Cloud。尽管如此,你还是可以不选择 Office 365 而直接购买 Offce 2016,旧有的 Office 授权也没有任何影响。任何的转型都是麻烦。有些软件的转型给老用户提供了空间,可以照常使用或者优惠升级。不过 Ulysses 做得就比较绝了,尽管花钱购买了的老用户可以优惠订阅,但如果他们升级了版本,就必须在 14 天试用期内更新订阅,否则自己的文章就无法继续编辑;另外,用户的文章也没有好的方式导出。这算是一个极度损毁开发商信誉的事情,你至今在 Mac 和 iOS 的 App Store 里的 Ulysses 页面下,都能看到大量表达愤怒的评论。订阅制和买断制哪个更好,开发者、平台和用户争论至今也没有什么结果。不过自己花钱购买了产品,到头来还是要被开发商牵着鼻子走,这实在是有些可笑。

    Ulysses 现在的价格是 4.99$(中国区 26¥)一月,如果按年订阅则是 39.99$(中国区 218¥)一年,订阅在同一个 Apple ID 登录的全平台可用。如果是学生,可以得到优惠,6 个月订阅是 10.99$(中国区 73¥)。你可以得到 14 天的免费试用。另外,SetApp 也包含了 Ulysses,对于同一个 Apple ID 登录的 iOS 设备上也可用。

    iA Writer

    iA Writer,连同 Byword、Quiver 等在内的一系列写作软件,常被当作是 Ulysses 的竞争者。其中 iA 应该是最贵的,也是最老牌的,历经多个版本变迁。本文即是用 iA 写作的。相比 Ulysses 或者其他软件来说,iA 的可定制性很少——只能从两种等宽字体中选择,字体大小也只有三个等级,颜色主题除了日间模式和夜间模式外也不能调。设置页面可以用简陋来形容。

    iA Writer Sample

    很多人调侃说,自己已经买了的软件,就会想方设法找到它的优点。这话不假。因为潜意识里,对于已经购买的软件,自己就不会把价格纳入考虑因素了。也许对于我,iA Writer 也是这样。每次用 Filco 的红轴键盘和双拼输入法,搭配全屏模式的 iA,写作起来的体验非常好。你可以在 iA Writer 的官网通过视频观看到 iA Writer 的使用方式和界面。

    iA 基本支持 MultiMarkdown 语法,除了基本的 Markdown 标记之外,还有注脚、目录、任务列表、表格和 LaTeX 公式等扩展内容。最新的 iA Write 也加入了标签功能。iA 支持类似 Ulysses 和 Typora 上的打字机模式。同步过程是使用 iCloud Drive,意思是每篇文章其实就是一个 txt 格式的文本文件,并且可以在 iCloud Drive 上看到和用其他编辑器编辑。一开始我有点不满意这个做法,认为类似 CloudKit 的数据库同步方式更加易用和干净。不过 iA 支持新建文档的时候自动使用 Markdown 标题作为文件名,这一点也逐渐冲抵了基于文件的同步方式的不便。不过在处理中文全角标点作为标题时,文件名中的符号会被自动转为半角,这是个小问题。另外,可能因为文本文件本身不大,iCloud Drive 的同步还比较流畅。当然,你还可以选择用 Dropbox 同步或者直接在本机的某个目录下保存文档库,在你能够流畅连接 Dropbox 服务的时候,可能体验更好。iOS 端当然也支持对 iCloud Drive 上内容的编辑,并且甚至可以在里面打开你 iA Writer 目录之外的任何文件(即使需要用户自己操作,我也很惊讶 iOS 居然提供了这种接口)。使用基于文件的方式,即使有一天 iA 开发者作死不让你继续使用了,你也至少可以从目录导出你的所有文件。以 Evernote 为代表的数据库模式和若干编辑器代表的文件模式,实在是各有各的好处吧,也许有一天,会有一个兼具两者优点的应用出现也说不定。

    iA 的另一个优点在于支持 Windows 和 Android 平台。由于我没有用过,这里就不多讲了。缺点包括统计中文文章字数的时候有问题(Bear 也不对,除了 Word 很少有软件能把这事做好的),语法突出不支持中文(如果你使用英文写作,这倒是个很有意思的功能)。目前看来他们也不想把应用改成订阅制。(Ulysses 遭遇到的阻力应该会冲击很多开发者对订阅制打的算盘)他们的博客上每月会发布一篇文章,其中多数跟产品本身没有关系,质量倒是挺高的。iA Writer 采取买断制,Mac 版价格在中国区是 198¥,iOS 版在中国区是 30¥。Android 和 Windows 的价格没有看到,似乎 Android 采取了免费下载搭配 App 内购买的方式,Windows 版本可以免费试用,购买价格 19.99$。

    Org-mode

    总的来说 Org-mode 是众多笔记和日程解决方案中比较硬核的类型——没有界面,基于纯文本。当然,最简单的方式也就是最靠谱的方式,虽说 Org-mode 只是一个纯文本界面,不过神之编辑器 Emacs 对它有着绝好的支持。现在的 Emacs 内置对 Org-mode 文件(后缀名为 org)的解析,连带若干快捷键。如果你安装了 Spacemacs,那么功能就更是丰富。

    Example of Org-mode

    所以 Org-mode 到底是什么呢?简言之,类似 Markdown 的一种纯文本标记语法。Org-mode 基于大纲,常被用于笔记和待办事项。你可以切换大纲中任何一个条目的 TODO 状态,或者查看有哪些任务的 Deadline 是今天。不同于分裂的 Markdown,Org-mode 的发展基本都以 Emacs 为主要(几乎是惟一好用的)实现。借助于 Emacs 优秀的扩展性,Org-mode 的纯文本可以说被玩出了花。当然,即使没有 Emacs,用任何一个文本编辑器都可以手动处理 org 文件。而在移动平台上,有 Mobile Org 和 beorg 等 App 基于云盘提供编辑和同步 org 的功能。相对来说,beorg 是比较好用也在持续更新的一款,但就 Org-mode 的「大纲」本质而言,beorg 做得还不够,其团队思路更像是把它当作一个提醒事项和日历管理软件。

    Evernote

    Evernote 的出现早于大部分现存的电子笔记软件,以至于说起笔记软件,很多人第一时间想到的都是 Evernote,或者它的中国版,印象笔记。Evernote 带有基于笔记条目的同步功能,以笔记本和笔记本集(Stack)组织笔记,同时也具有扁平化的标签系统和搜索功能。Evernote 自带的编辑器对非技术人员来说应当很亲切,容易想起熟悉的 Word。不过如果用得稍微多些,就会发现这个编辑器实在是乏善可陈,基本的富文本编辑没做好,也没有类似 OneNote 一样的语义化文档结构(一级标题、二级标题、引言……),更勿谈 Markdown 支持这种功能了(尽管有马克飞象这类第三方 App,现在印象笔记也支持了 Markdown)。

    Evernote 和印象笔记的一大优势在于,多年的发展使其拥有了一个良好的生态圈,很多应用都支持将内容直接发送到 Evernote。另外,Evernote 还有用一个强大的 Web Clipper 插件,能够从网页中抓取正文自动保存到笔记本中,这也就是很多人说其「善于收集」的原因之一。不过从这个角度,Evernote 更像是一个云端同步的文档库,而不是全功能的笔记软件。从这个角度出发,也许一个具备全文搜索功能的文档管理软件也可以部分取代它的作用。

    Evernote Sample

    Evernote 和国内的印象笔记都可以免费使用,不过免费用户有诸多限制,比如一个月同步流量不得超过 60M(如果你同步的基本都是文字,这个数量倒是基本够用),只能同时两个终端登录,以及不可以使用在 PDF 内搜索等高级功能。付费采用的是订阅制,印象笔记的个人用户有标准版和高级版两个付费级别(Evernote 似乎只有高级版和企业版之分),标准版把每月流量提升到了 1G,不限制终端;高级版每月流量 10G,并且加入了一些高级功能。国区的印象笔记和 Evernote 是完全不同的两个账户体系,甚至可以用同一个邮箱注册 Evernote 和印象笔记两个不同账号。Evernote 高级版年付 5.83$ 一月,月付 7.99$ 一月。印象笔记标准版年付 8.17¥ 一月,高级版年付 12.33¥ 一月。

    需要注意的是,自 2018 年 8 月起,印象笔记已经从 Evernote 国际版独立出来。中国区的 App Store 只能下载印象笔记,而外国区只能下载 Evernote,图标也不一样了。Markdown 等新功能仅限中国版独有。Evernote 国际版的经营状况不佳,时常有裁员的消息,不过国内版还是暂时不用担心倒闭的问题的。由于监管原因,国内版的印象笔记是不支持笔记分享功能的。

    OneNote

    OneNote 是微软出品的笔记软件,据传其历史可以追溯到 docx 格式出现之前的 2003 年,最开始是作为 Office 套装的一部分销售的。一开始,OneNote 是一个像 Evernote 一样的电子笔记本,并且针对企业内部协作的场合进行设计。(一开始我还没有意识到为什么企业内部会需要一个笔记软件,直到后来我知道了传说中惹得天怒人怨的 Lotus Notes)不过实际上,OneNote 的使用体验让它比 Evernote 更像是一个现实生活中的笔记本,而不是数字资料集。

    Example of OneNote

    OneNote 采用了三层结构,从笔记本到节再到页,而页可以像书的章节关系一样无限划分出子页。这样的结构相比于 Evernote 而言明显具有更强的结构性,适合写书或者课程笔记一样的场合。但 OneNote 实在是太过自由,每个笔记页都可以有若干个像白班上的贴纸一样的块,每个块都可以自由移动甚至和其他的块重叠。这很像真实笔记,不过对单纯想数字化输入和保存一些东西的强迫症来说,这反而是个不便之处。(想想 Markdown 为何如此流行,它在富文本面前到底有何优势?)手写的内容和公式、录音等等可以随意插入笔记页的各个角落。手写和图片内容甚至可以被搜索(Evernote 高级版也有这个功能,没有比较过哪个更强大)。如果你没有类似的强迫症且从接触电脑以来就习惯 Office 软件的界面,那 OneNote 是很适合的笔记解决方案。

    不过笔者还是倾向于把 OneNote 单纯作为一个数字笔记本。OneNote 为平板和手写笔做了很多适配和优化,搭配 iPad 和 Apple Pencil,书写的体验非常舒服。如果你用 Surface,也可以好好试试。毕竟,相比 iPad 生态下的其他手写和录音笔记软件,OneNote 完全免费(只要你 OneDrive 还有足够空间),还是大厂出品,更新频繁,为什么不用呢?很多人抱怨 OneNote 同步速度慢,除了微软服务器和本地网络的原因之外,很可能也是因为 OneNote 是按照节为单位进行同步的,笔记稍丰富一些一次同步就可能是十多 MB,不光慢还是流量杀手。OneNote 的另一个大问题是关于默认字体的,在 Mac 和 PC 上谁用谁知道。

    Bear

    Bear 是苹果生态下的一款书写和笔记软件,荣获 2016 的 Mac 年度最佳 App 大奖。Bear 令人惊讶的是它的简单风格和明显经过设计的界面。默认的 Avenir 字体在外行眼中的第一感觉就是「洋气」。无独有偶,Bear 也没有使用 Evernote 风格的富文本编辑器,而是在 Markdown 基础上做了些小修改作为自己的语法,以每篇 Markdown 文本作为笔记条目。(可以打开「Markdown 兼容模式」)

    Bear 既不像一些文本编辑器一样不刻意突出 Markdown 元素,也不像 Typora 一样所见即所得,而是在文本中突出了样式。有很多人可能不喜欢这样的编辑器,但它可以帮助很多不熟悉 Markdown 格式的人慢慢上手。

    Sample of Bear

    Markdown 不是一个陌生的东西,Bear 除了界面上的美观之外,在笔记的组织方式上也有创新之处。传统的笔记软件,比如 Evernote,试图把现实生活中的笔记本映射到软件中,创造了「笔记本」这样一个概念,和笔记条目构成一对多的关系,然后为了多元化组织方式的需要,每个笔记可以有若干个标签,标签和笔记构成多对多关系。Bear 则直接放弃了笔记本这个做法,全部使用标签。除了每个笔记可以有多个所属标签之外,标签还可以层层嵌套,比如 job/code 就意味着 job 标签下的 code 子标签,一个以此为标签的笔记既可以在 job 下也可以在 jobcode 下找到。看似有些偷懒,实则打破了到底要创造哪些笔记本这一难题。书写体验这个事情,见仁见智,不过很多人把 Bear 当作轻量任务管理工具,或者做为增强版的备忘录。Markdown 用以写作当然很棒,但用来做知识管理就有些捉襟见肘了。

    Bear 采用了苹果推荐的订阅制模式,如果不订阅依然可以单机使用软件,但无法同步、使用高级主题和导出至更多格式。但它的同步基于 iCloud,所以目前也只支持 iOS 和 macOS 设备。不过这个把依赖苹果 iCloud 服务作为收费功能点的做法,严格来说似乎违反了苹果的规定。Bear 现在的价格是 1.49$ 一月初次附赠一周(中国区 10¥),14.99$ 一年初次附赠一月(中国区 103¥)。Bear 的团队来自意大利,主要成员只有 3 人(最近好像增至 6 人了),虽然不用担心它倒闭,但更新速度的确不快,Web 版也遥遥无期。

    WorkFlowy/Dynalist

    这两个软件属于笔记型软件中的奇葩,不是传统的所见即所得或 Markdown 文本,而是采用大纲的形式组织数据。WorkFlowy 算是这类软件的始祖,每个用户一打开就只有一个文档,没有办法新建和管理。文档可以是无限层级的,并且可以采用「聚焦」模式,即把大纲中某个节点作为当前视图的根节点。以此方法,用户的所有数据都组织成一棵树。WorkFlowy 并没有提供更多更复杂的功能,在大纲和聚焦的基础上,用户可以把某行标记为「完成」(就是打上删除线)或者在某行下方加上注释,用户也可以把常用的某个节点收藏起来方便快速聚焦。WorkFlowy 的功能基本就是如此了。

    Dynalist 相比 WorkFlowy 有一些改进,比如加入了 Markdown 和待办事项支持、支持多文档等。不过最重要的是,Dynalist 的免费版本即可无限创建节点,而 WorkFlowy 对免费用户有每个月 100 节点的限制(可以通过邀请的方式扩容,每邀请一个用户双方都可多获得 100 的每月免费节点)。

    Sample of WorkFlowy

    很多用户在尝试过功能丰富的富文本笔记软件之后可能会觉得基于大纲的方式太过简单,不过多尝试一下就能体会把思维整理得高度结构化的快感。这种模式特别适合于在开展某些项目或者写作文章之前为内容列好提纲,也可以用于组织某些结构化的笔记,比如读书笔记等。不过笔记这件事没有银弹,在其他一些用途上,可能大纲类软件的确是太过简单了。

    WorkFlowy Pro 版本的价格是 4.99$ 一月,49$ 一年。Pro 用户拥有无限的储存空间、备份至 Dropbox、带密码的协作、更改主题等功能。Dynalist Pro 版本价格每月 9.99$,如果年付则是每月 7.99$,包括自定义 CSS、Google Drive 和 Dropbox 备份、无限量创建书签、版本修改历史、Google 日历集成等功能。

    备忘录

    对很多苹果用户来说,备忘录可能才是每天使用次数最多的软件。尤其是 iOS 和 macOS 备忘录间的同步,对多设备用户而言,是个非常有吸引力的功能。备忘录和 Evernote 不同,本身的编辑界面没有明显的样式调整工具栏,在样式按钮下也只有寥寥几种选项:大标题、标题、正文、等宽和序列。但备忘录本身是的确支持带各种样式的富文本的(尽管不如 Evernote 一样完整)。在「编辑」菜单中的「格式」选项中,可以看到,可以设置备忘录内容的字体、大小、颜色、基线、对齐,加删除线,乃至调整缩进。另外,备忘录还支持在笔记中插入待办事项清单——就差个提醒了。

    在 iOS 上,iOS 11 对控制中心的改进可以让用户快速从任何界面启动备忘录;而在 Mac 上,备忘录也几乎是启动最快的笔记软件了。备忘录的同步完全依赖于 iCloud,这意味着只要你的 iCloud 有足够的剩余空间,同步的设备数量、同步流量都不是问题,完全免费。在 iOS 上,备忘录还支持扫描文件导入备忘录。这一功能和 Evernote Scanner、Office Lens 或者扫描全能王等专业应用比起来还是欠了点火候,不过在光线充足的时候已然够用了。如果你更新了 macOS 10.14,系统备忘录还支持从你的 iPhone 或 iPad 上拍摄扫描照片直接导入。

    系统备忘录也是支持从 Evernote 导入的,在「文件」菜单中的「输入」弹出的选择文件对话框里,选中 Evernote 导出的 enex 文件并打开,备忘录就能自动把里面的笔记转换成备忘录的格式放到一个统一目录中。样式可能会有丢失但大体结构还在。不过备忘录不支持导出功能,只能输出 PDF。好在备忘录支持同步其他互联网账号上的备忘录功能,因此你可以用备忘录连接你的 QQ 邮箱或者 Outlook,把 iCloud 或本机上的笔记条目移动过去,以此达到导出目的。不过要注意的是,邮箱账号的笔记本支持的文本格式相当有限,直接移动过去一些样式和链接可能会丢失。

    其他

    除了以上提到的几个(或许大名鼎鼎)软件之外,还有许多(号称)与生产力相关的 App 自己或多或少体验过。

    Simplenote

    Simplenote 如其名,的确简单。整个软件的功能就是,书写纯文本笔记,添加标签,然后同步。你可以选择开启 Markdown 模式,这样你就可以查看当前条目的 Markdown 预览。每条笔记都可以查看历史版本。或许正因为完全依赖纯文本,Simplenote 的同步速度相当快(如果没有连接问题)。目前,Simplenote 支持大部分平台,从 Windows 到 Mac 和 Linux,以及若干移动平台。Simplenote 是完全免费的,运营它的公司是 WordPress.com 平台的母公司,因此暂时还不用担心公司哪天倒闭了的问题。不过 Simplenote 的格式太简单了,连 Markdown 的支持也不是很完整。

    Agenda

    Agenda 给人的感觉像 Bear,也是备受赞誉的后起之秀。相比于笔记或者书写软件,Agenda 更侧重的是任务和日历的集成。每个项目中的单个条目都能连接到日历,同时顺便做做笔记。笔记语法基本和 Markdown 一致,但直接以带样式文本显示。不过 Agenda 目前看来编辑器还不太成熟,容易出现卡顿和样式出错等 bug。Agenda 的更新模式很特别,就像 Sketch 或者早年一些杀毒软件,按年付费,类似订阅制。不过如果停止付费,软件依然可以正常使用,只是得不到新的高级功能更新。对这种级别的软件而言,相比无脑订阅制,这个付费模式看上去更有责任感。目前一年的价格是 163¥,支持 iOS 和 Mac 平台。

    Agenda 的问题在于它的定位。它想统一日历、提醒事项和笔记的功能,但它似乎这几个都没做到很好。类似的应用有 Noteplan,也试图将笔记和日历统一到一起。可我们日常真有那么大「笔记随日历走」的需求吗?

    Dropbox Paper

    Dropbox Paper 是 Dropbox 推出的在线文档服务。这个服务支持用 Markdown 语法书写内容,在网页中也支持对内容进行全文搜索。但它作为笔记而言还是缺少了一些资料管理方面的功能。而且就官方定位而言,Paper 更像是 Google Docs 的竞争对手,而不是 Evernote 这样的软件。如果你有协作方面的需求,可以尝试一下 Paper,产品体验还不错。

    Typora

    Typora 不同于很多 Markdown 编辑软件——它成功地把所见即所得特性和 Markdown 的「干净」整合到了一起。在编辑器里打出两个星号,后面的文字就会自动变成粗体。在边界处按退格键删除字符时,删除掉的也不是粗体字而是星号。Typora 目前免费,作者曾宣称到正式版会开始收费,但正式版目前看来遥遥无期。Typora 跨主要的桌面平台,支持比较复杂的 Markdown 扩展,包括 LaTeX 公式、表格、目录等。另外,它还支持更换主题,自己把对应 CSS 放到指定目录就好。Typora 是一个优秀的 Markdown 编辑器(如果你喜欢这种 WYSIWYG 风格),尽管处理大文件时还是有明显卡顿感,但作为笔记软件,还是有所欠缺,一在文档管理,二在多端同步。

    生态

    计算机诞生的最初目的即是帮助人提高解决问题的效率,从 DOS 时代到今天,各类效率软件层出不穷,大名鼎鼎的也不少见。像 Things、OmniOutliner、OmniFocus 等软件本文没有一一介绍,读者有兴趣可以自己发掘。

    如果你平常主要使用的软件基本是 QQ、微信,那么用什么手机和电脑对你而言不会有太大影响,因为这些软件对多个系统都有良好的支持。(Linux QQ 和 Windows Phone 看来要除外了)但如果你接触这类效率软件比较多的话,就会很容易被某个平台所绑定——即使认为它贵或者对它不满意,也不得不继续使用,因为数据都放在这些软件和服务上面。

    所以,在选择软件时,除了软件本身的体验和功能之外,还得考虑它们的可迁移性。一般来说,自己提供后端存储的软件都具备网页界面,所以都可以跨平台。但反过来,这些服务的寿命几乎不会超过苹果、微软、Google 等巨头,维护人员的技术也不会有它们的高。如果数据能够存储在这些提供个人云盘服务的服务器上,可能更加可靠。一部分软件客户端是跨平台的,在更换手机或电脑的时候不用过分担心。但对于一些只在某些系统上有版本的应用(苹果生态居多),就要着重考虑它们是否有方便的导出功能了。基本上,数据和应用在一个生态里融入越深,要迁移出去的成本就越高,这是难以彻底解决的矛盾。

    没有一个软件能够让所有人满意,也许理想的笔记形态离我们还有一段距离。不过如果你有空反思过滤每天收到和发出的信息,勤整理笔记本,也许会发现对信息处理工作流的热忱,只是种没什么必要性的狂热。购买正版软件提高效率是好的,但它并不等于数字消费主义。

    毕竟,软件不是最重要的,作品才是最重要的

  • Siri Shortcut 初体验

    Siri Shortcuts 始于之前的 Workflow。实话说,自己当时对 Workflow 的印象属于鸡肋,由于 iOS 系统的限制,很多功能做不到,也需要在 Widget 去启动。相比 JSBox 的直接明了,功能确实差了不少。而自己却连要用 JSBox 做什么都没想明白,更勿谈 Workflow 了。因此,尽管苹果收购了 Workflow,我也没有对这个应用抱有太多注意力——手机上能做到的所谓效率和自动化实在太有限了,在这上面刻意追求有些错了方向。同样的,还有 IFTTT 这样的应用,不过它的着重点和 Workflow 似乎不太一样,有许多 Applet 是完全不依赖本机的。(比如每个 Google Voice 用户都会尝试的保活 Applet)

    今年的 WWDC 推出了 iOS 12,其中将原来的 Workflow 和 Siri 整合到了一起,作为一个新的系统功能叫做 Siri Shortcuts。当然,还是需要额外安装。我一直觉得,Siri 是个有些无用的东西。(当然了,所有的语音助手在这一点上都没什么根本区别,而 Siri 的语音合成还明显不如微软的 Cortana)今年 Google 在开发者大会上用语音助手自动点餐的 Demo 震撼了许多人。而苹果终于选择放下曾经的固执,朝着另一个方向,在 Siri 有根本性的突破之前,走一走 Amazon Echo 的路子,让用户自己定义自己的语音助手。

    把 Siri 和 Workflow 结合起来,意味着你可以自定义语音指令,驱使手机去帮你做一些事情。这无关所谓真正的「智能」,不过这确实让它变得更加实用了。另外,Workflow 变成 Shortcuts 之后,显得更加官方化,有更多苹果生态下的 App 开发者主动为它提供了接口,这也使得它的功能边界更拓展了一些。

    这里是个有趣的例子,我们想要检查一个人有没有发布新的微博。不想每次都点开,甚至像我这样把微博 App 删除了的人连特别关注都没得设置。我们想用 Siri 做到这一点。就以@带带大师兄为例吧。

    新建一个叫做「孙狗微博」的 Shortcut,在里面随便设置一个喜欢的颜色和图案。

    New Shortcut

    点击进入编辑内容。首先我们需要一个信息源。Shortcuts 有 RSS 的组件,但微博并没有给我们提供官方的 RSS 接口。或者可以在自己的一台 VPS 上写一个脚本定时刷新内容然后暴露一个 API 出来,但这样也太麻烦了。Shortcuts 支持在给定页面上运行一段 JavaScript 代码并返回结果,这个不错。不过这里发现了更好的选择,即微博的 API。这个 API 是从网页调试工具里发现的,带带大师兄的微博内容在这里,以 JSON 格式呈现。

    我们需要的字段主要有两个,一个是最新一条微博的发布时间,一条是最新一条微博的内容。方便的是,微博已经帮我们处理好了发布时间的可读性,近在 3 小时前发布的微博,结果会显示为 3 小时前而不是具体的时间。

    JSON Format

    好了,我们现在可以使用 Shortcuts 的 URL 组件,将字符串「转换」为 URL 对象。然后用「Get Contents of URL」获取内容,注意是 GET 方法。这个的结果就是这个 API 返回的字符串了。

    URL Component

    接下来需要获取指定位置的数据,在 data/cards/2/mblog 中。我们先将之前的字符串利用「Get Dictionary from Input」转换为 Dictionary,然后一步一步拿到数据。注意,这里的 List 索引是从 1 开始的。

    Get Dictionary from Input

    mblog 这里的时候,因为我们要用两次,所以先保存到一个变量中,用「Set Variable」,这个组件的输出就是变量的值。

    Set Variable

    于是乎就可以把时间先念出来了,用「Speak Text」。我手机的语言是英文,所以这里的「Language」不能为默认,而是要指定成中文,不然 Siri 会念不出来。如果系统语言是中文的话应该不存在这个问题。

    Speak Text

    接下来也一样,先取出之前保存下来的 mblog 的值,因为 JSON 里的原结果是 HTML,所以要先转换一下,不然 Siri 就会把标签符号一个一个念出来了。话说,这里还有富文本、HTML 和 Markdown 的相互转换工具,很有用。

    https://i.e7s.me/4a510892-b64b-4e5c-a17a-41bacc500192.jpeg

    最后我们在设置里指定一个语言指令,比如「孙狗发微博了吗」。

    Text Command Content

    看起来运行得不错。

    Demo

    自 iOS 12 正式版发布以来,有很多应用都专门加入了针对 Siri Shortcuts 的更新。在选择组件的界面,我们也可以看到有许多应用提供的接口,包括我手机并没有安装的 Ulysses 和 Drafts。结合应用的 x-callback-url,能做的事情可以很多,只要有足够的想象力。当然,像我们这里这样简单的流程,已经需要比较复杂的定义和操作。如果真的有更复杂的需求,还是用 JSBox 和 Pythonista 这样的应用好一些。不过,目前这两个应用还不支持从应用返回结果,只能执行特定脚本然后返回输入。希望未来他们能够做得更好。

    最后,我把这个 Shortcut 放到了 iCloud 上

  • 高考小记

    不知不觉,已是高考结束的第四年了。当初一起参加高考的同学一个个都开始分享自己穿着学士服的毕业照。有些还认得出,有些已经换了个样子。大概知识的冲刷都让大家变得有了内涵。不过我们几个男生,不管讲究的还是不讲究的,从照片看上去还是一个样。倒也只能安慰自己,男性往往成熟得更晚一些。大二的时候还会看到有同学自信地穿着不合身的西装,似乎现在都意识到了,暗示自己是学生会干部,也不过是在朋友圈里多拿几个赞而已。

    对于四年前那场考试及其前后的经过,记忆早就变得模糊,也不知道是身体机能在下降,还是时间真的可以抚平痕迹,不光是自己作留下的短暂伤痕。刚上大学那会,大概每隔个礼拜睡觉就会遇到「回去高考」的噩梦,有时候还附带走错考场这样的大礼。后来慢慢就没有了。直到最近,偶尔又开始陷入这个奇怪的梦境中难以脱身。联想自己最近的心理状态,我才意识到梦的逻辑未必那么简单。辛苦也好,不公平也好,高考都不是一场单纯的灾难,也不是压力的重点和释放的起点,十二年以来的课堂在我们心中种下了强烈而又时常不自知的暗示——高考是一场改变,或者说,革命,如果你愿意把人生看作一段历史的话。它可能将你带上高峰,也可能推你坠入深谷。安居乐业的人民是不会革命的。所以当自己不满现状的时候,高考之火又会在熟睡的深夜燎原。尽管高考早已结束,但它实在是和「改变命运」四个字形成了过于深刻乃至本能的心理关联。也因如此,许多人高考期间并不沉闷忧郁——大学能有高三苦吗?其实,中考前我也有过类似想法。

    在网络上,以高考为关键字搜索,去除大量有关辅导和招生信息有关的材料,会发现它连带着太多人的故事。大片的文字叙述了不同的高考经历,以及他们的人生如何因为这总和不到十个小时的考试而转折。再想一想肯定有更多的人没有讲出自己的经历,不禁窒息。同样具备大量信息量和故事性的关键字,我只能想到一个,即汶川地震,但沉重许多。汶川地震十周年的时候,我在网上看到一个视频,是地震发生时,成都某高校一学生在宿舍内拍下的场景。我无法确信他那位躲在桌下喊「老婆我爱你」的室友是否已经和同样的人结婚,不过我有足够理由认定,他们今天都还活着。他们现在在哪呢?这问题值得思索,因为当我写作时在图书馆坐在我旁边的同学,我也有足够理由认定他十年后还活着。我会去想他的故事吗?或者说,许多时候我们也不会思考,自己所处的位置,十年前是谁在做什么呢?高考如是。如果当初一念之差,我猜对了砾石到底是在大河的凸岸还是凹岸,是不是我真的就不在这里了?

    不过也不用这么惆怅。尽管我的去向像个随机事件,我还是相信无论我的结果如何,2018 年的社会还是今天这个样子。所以,即使高考结果相异,我大概只是在另一个地方逛 V2EX,帮另外的人做网站而已。其实更叹息的,是时间的消逝远远快过自我的体会。说好的大好时光,说好的未知远方,到今年 6 月都应该画上句号。一切历史都是当代史,谈古无非为了讽今,如果自己对现在的自己满意,「此间乐,不思蜀」的话,怎么会追忆当年呢?

    尽管在高考后的几天乃至几周里,许多人都会纠结自己那道简答题是不是正确,机读卡有没有填错。但是,再过一段时间就会发现,真正在乎高考题目的,只有又从头开始教高一的那群老师。高考体系的某种荒谬也在于此——如果它真的应该重要到决定你的人生,又怎么会至于过段时间你就忘得干干净净?好像也不全是,每次把碎玻璃扔到垃圾桶的时候,我都会想起那个突降暴雨的上午,想起迄今为止吃过最舒服的糖醋排骨。

    有时候,不高考的人反而比高考的人更认为这是场残酷的修行。想一想,在大学里,越来越难以回到考前所在的奇妙的平衡状态,又紧张,又有稳固知识支撑的自信。在事情变得随意,前途变得迷茫,心情变得焦虑的今天,应该很多人都会回想起几年前,有远虑无近忧的惬意下午吧。每一件小事好像都很有趣,都让自己想起来不虚此行。

    不过,我还是不同意「高考是人生的必修课」这句话。混淆因果是人的本性。我们所怀念的、叹息的、回忆的,都是高考这个体系下一群十多岁的少年聚集起书写的故事,不是四张考卷本身,不是匆匆依赖一个分数决定这件事本身。教育问题也是社会问题,不存在全面完美的答案。上大学之前不能理解什么叫「教育资源」,后来意识到,是资源就是稀缺的。有很多人,可能跟我一样,批评高考批评应试教育但还认为这个体系会继续存在,原因是,被坑了才发现大学教育问题更多,更需要资源,更应该改变。

    所以,偶尔有那么一瞬间也会回忆高考,回忆高中。别以为我是自虐,我只是想重来。

  • 说说隐私

    隐私很重要。每个人都知道。可什么才算隐私呢?小时候和同学课上传的小纸条、学期结束给老师的评语、自己拍摄的照片……给定一个事物,我们往往能对它属不属于隐私作出判断。但是对于「哪些是我们的隐私」,我们好像并不如「我家里都有哪些人」清楚。

    好吧,按照惯例,先来看看隐私一词有无相对准确权威的定义。

    任何人的私生活、家庭、住宅和通信不得任意干涉,他的荣誉和名誉不得加以攻击。 人人有权享受法律保护,以免受这种干涉或攻击。

    此定义来自于《世界人权宣言》,亦算作是各国政府制定相关法律时需参考的材料。宣言起草于 1948 年,时值二战结束,各国都已承受过人权被肆意践踏的惨烈后果。然彼时尚无成体系的数字信息存储,更无如今社会无孔不入的网络通信。因此在数字时代,隐私的外延已经有了发展变化。而巨头和权威对个人数字化隐私的监视,也让更多的人重新思考如何用新的方法保护隐私。

    数字化时代的特点

    隐私的本质保护的是一种信息,数字化时代的信息生产、保存、传播方式都有了显著变化。因此要分析数字化时代的隐私,先考虑一下数字化时代信息的特点。

    • 零复制成本。说信息没有复制成本可能不准确,不过用旧的眼光打量现代的信息复制,就会发现实在是太容易了。基本上,只要能获取到一份源数据,复刻出千份万份然后分发,不过是点点鼠标就可以解决的问题。当然在商业用途上,已经有了包括 DRM 以及 Netflix 防截图、中心服务器验证等技术在内的版权保护方案。然而个人的信息与这种商业批量销售的数字内容存在本质区别。
    • 传播路径长。一个信息要从一处到达另一处需要经过活跃在互联网协议栈不同层的中间节点,这些节点都有可能篡改、保留信息。
    • 终端结构复杂。多数人生产信息和连接互联网使用的都是个人电脑及移动计算设备(平板电脑、手机),这些设备大部分由特定商业公司开发并封闭源代码,不排除留有后门的可能,并且存在被恶意程序攻击的危险。
    • 数据在云端。出于同步方便或者本地储存空间的限制,越来越多的人选择将数据存储在服务商提供的云存储空间当中,并利用其提供的额外功能进行分享、编辑等操作。这些数据通常并不能保证被严格加密和保护。
    • 社交化蔓延。同传统软件是纯粹的生产力范畴相比,数字化时代的软件往往带有社交属性,一个人在社交圈子中几乎不可能完全避开这类软件。

    而在国内的网络环境下,信息的保存和传播还具备一些额外特征。

    • 匿名性弱。现行几乎所有国内网络产品账号都需要直接或间接绑定手机号码,而手机号码又严格实名。部分网络应用还要求提供额外的身份证及真人照片。
    • 体系封闭。许多国内服务商对 Web 网页端抱有抗拒的态度,部分服务甚至完全不提供网页版本。
    • 容易终止服务。许多云端服务的用户都面临着服务终止的危险,费用难以退回尚是小事,数据导出的周期也很短,甚至出现无法追回数据的情况。当然,国外也有许多例子。不过由于国内网民的消费习惯,个人云服务往往只是作为互联网巨头的边缘产品,少有专做云服务的企业,因此这些服务被「战略性放弃」的可能性比较高。
    • 泄漏风险高。国内的个人信息泄漏和买卖已经形成了一条完整的灰色产业链,用于推销、诈骗等领域。如果个人相对私密的信息泄漏且形成闭环,造成的社会风险极高。

    个人资料的保护

    信息本身是没有意义的,要被特定的人利用之后才能体现出特别的价值。每个人在生活当中都会产生或获取大量的信息,而这些信息的特性也各自不同,因此不太可能一视同仁地去保护。

    信息保护的本质

    那么,要保护,首先需要厘清保护信息这件事的内涵是什么。

    • 保证信息不能丢失
    • 保证信息不让不需要的人获取到
    • 在需要时,能够将信息安全地传递给另一方

    第一点其实没有什么好说的,在不考虑存储介质物理特性的情况下,保证信息不丢失的惟一做法就是备份。从 RAID 1 到 RAID 5 再到普通的硬盘复制,都是行之有效的备份方式。而对于云服务的使用者来讲,一个稍靠谱的服务提供商在数据冗余这方面都会比普通用户更加重视。只要不是在有意删改数据的情况下,一般不需担心数据丢失的问题。而像 iCloud 一样的云服务多数用作同步,在每台设备都留有一份副本,完全丢失的风险更小。

    这里需要考虑到数据是否属于常改动内容。如果纯粹出于归档的目的,放在自建阵列或者备用盘中更为稳妥;否则,就很不方便了。

    第二点和第一点实际上存在冲突。如果每一个介质都有一定概率被其他人窃取到数据,那么多个备份会让这个概率大大增加。好在信息技术为这一点提供了一条方便的方式,那就是加密。主要的操作系统都提供了可靠的磁盘加密方式,保证磁盘被第三方窃取之后,在没有密钥的情况下,数据不会被获取。现代的文件系统也提供了对单个文件或目录加密的功能。考虑到可移植性,也可以用 ZIP 等压缩格式带有的加密方式或者利用 OpenPGP 软件进行密钥加密。合理运用这些加密方式,基本可以抵御存储器意外丢失造成的数据泄漏风险。当然,早期 Google Chrome 隐私模式会提醒用户「注意你身后的人」。如果想让数据在被检查的时候还能匿迹隐形,做法无非是将内容混淆在普通的文件当中。这不是本文想探讨的主题。

    第三点本质上也是第二点的延续,不过面临的情况更加复杂,需要的应对方法也更加多。说到底,要解决这个问题,方法依然是加密。常规场景下,网络使用者接触到的无非是应用层级别的加密,以 HTTPS 为主。而新版本的 iOS 对于应用的网络请求强制要求使用 TLS 加密。对于浏览网页来说需要注意的就是不要胡乱添加根证书,适当时候需要删除本地中保存的不合适的根证书,以及及时更新浏览器。这里推荐 HTTPS Everywhere 这个浏览器插件,目前在 Chrome 和 Firefox 上都能找到。而如果你架设了一个网站,也请尽量申请一个免费的 TLS 证书,并配置好服务器,避免心脏出血等漏洞。另外一个场景可能是,在某些情况下,需要模拟 TLS 在一些不安全的应用上(比如电子邮件)传输加密的信息,解决这个问题的工具就是 OpenPGP 了。在网页端或者服务器端可以使用 OpenPGP.js 库实现生成密钥、加密、解密、签名等操作。在本地则可以使用 Gnu Privacy Guard (GPG) 完成这项任务。如果使用 Thunderbird 作为电子邮件客户端,可以利用 Enigmail 插件自动完成邮件的加解密和签名。这样就可以做到端到端加密了。不过对于电子邮件,需要注意的是标题是不会被加密的。

    一个重要问题是,在上述讨论的第三点中,与自己通讯的人是否是安全的?这实际上是很多人对加密技术产生的误解。加密只能保证你的信息安全地从你本地传输到了另一个地方不被第三方破解或监听,但你没有任何方式确保这个通讯者不会出卖你的信息。这实际上和加密技术没有任何关系。而在某些时候,这个信息是不得不交出去的,这是后面要讨论的内容。

    将隐私信息分级

    尽管对于普通人,一些简单的额外加密就可以保护自己的信息免受各种风险威胁。然而对不同的信息进行分类考虑和存储仍然是有必要的。一些数据泄漏可能仅会让你接到几个推销电话,而另一些则事关身家性命。

    • 极度危险并私密的信息。这些信息和数据不应该被放到网上,应该经过严格的加密后储存在安全的本地存储设备上。如果有特别的危险,这些数据甚至只应该牢记于心,或用只有自己才知道的方式混淆于普通文件中。
    • 私密但需要同步的信息,比如照片。这些数据可以放在自建的私有云服务上并经过加密存储,并且同步到备份硬盘或者加密后同步到网络服务商提供的对象存储服务中。如果你信任某些厂商(且认为他们短时间内没有倒闭的风险),可以将非极度隐私的内容放上去,毕竟它们的服务更加稳定,功能当然也更丰富。如果你只想做归档存储的话,用前面说的方法打包加密,几个地方都扔一份,也不危险。
    • 不能完全公开但不危险的工作资料。实际上存储这类资料要求的更多是稳定性以及分享、协作等功能。国内外都有相当多这类服务,或许跟着公司里大家的选择走是更令人愉快的选择。当然,如果你的工作本身就对信息安全要求很高,那还是认真按照更严格的规定操作吧。
    • 仅需要合适存储的资料。比如说你的学校今年的招生报告,跟工作和生活毫无关系。这些材料可以多份存储,哪里方面哪里来。

    当我们谈论隐私以及与其相关的一系列安全话题时,我们实际上更多想聊的是「私密但需要同步的信息」。

    iCloud 的替代品

    很少有苹果用户能够完全避开 iCloud 对生活的侵入。而 iCloud 引发的一系列事件让一部分人对 iCloud 的资料安全性不再信任,转而寻找其他的云端存储方式。这里聊聊几个常见的选择。

    • NextCloud (ownCloud 续作),如果你有过自己在 VPS 上搭建 WordPress 博客的经历,那么自己搭建 NextCloud 也不是什么难事。作为一个私有云,NextCloud 的功能实际上已经非常丰富了,除了最基本的文件存储之外,还支持分享、文件在线编辑、聊天、照片同步、笔记、日历等功能。手机端的 App 只需要输入自建服务 URL 和账号密码即可使用。总体来讲 NextCloud 还是很安全的,缺点在于需要自己搭建服务器,有一定的技术基础,速度可能也没有大公司服务的速度快。同时手机端照片管理的功能远不如 iOS 自带照片应用强大,如何安全地备份数据也是个令人头疼的问题。
    • OneDrive,其实连同前身 SkyDrive,OneDrive 已经运营有一些年头了,只不过免费容量一降再降实在让人不满,连笔者之前购买 Windows Phone 手机赠送的空间都砍掉了。OneDrive 现在和 Office 365 计划有联系,并且 OneNote 笔记本内容实际上也存储在 OneDrive 网盘当中,所以暂时不用担心它倒闭。毕竟是微软出品,OneDrive 上在线 Office 文件的编辑功能自然是绝佳,微软也试验过一系列智能照片管理的功能。Windows 10 已经集成了部分 OneDrive 支持。如果用过的话就会知道,最大的问题就是速度太慢。然后是空间太小(如果买了 Office 365 倒不存在)。手机端 App 的使用体验比 NextCloud 好,不过赶不上 Office 系列其他产品手机端的水准。
    • Google 云服务,Google 提供了包括照片、云端硬盘、文档等多款 App 来涵盖手机端云应用的功能,体验还是很不错的。微软和 Google 都是优秀的 iOS 应用开发商。免费的 Google 云盘存储空间是 15 G,值得一提的是,如果同意照片采用「高画质」而非「原文件」存储(也就是可能会稍微损失一点质量),照片的存储空间是无限大。Google 在照片应用中使用了大量人工智能技术,例如搜索 sunset 就会在上传过的库中筛选出夕阳景色的照片。根据 Google 的习惯,照片这类服务存在被砍掉的可能,不过参考 Google Code,真到了时候,还有足够机会迁移。最大的问题是在国内访问不便。
    • Dropbox,笔者几乎没有使用过 Dropbox 云盘和同步功能。Dropbox 的免费空间为 2G,收费扩容的价格也比较高。Dropbox 的独特之处在于它的增量同步和共享功能。许多第三方应用也支持通过 Dropbox 同步。和 Google 云服务一样,Dropbox 在国内访问不便。

    其他诸如 Amazon Drive 之类的服务在国内还不是特别成熟。而国内公司的云盘和同步服务质量不太令人满意。国内网盘服务普遍不盈利,朝令夕改,容易出现网盘关闭,数据难以找回的情况。在认真考虑到隐私状况的情况下,这些网盘在工作和娱乐上还是可以轻度使用或者做某种程度的备份。

    信息安全之外

    中学的物理题情景总是发生在去除各类难搞的干扰因素之后形成的「理想状态」中。现实往往比理想状态复杂很多。隐私保护亦是如此。信息安全的知识不能确保每个人都能够生活在安全和隐匿之中。即使信息以安全的形式传输和保存,个人隐私依然存在风险。

    • 不得不将信息公开或发送给不可信任的对方
    • 信息被针对性地破解或被胁迫解密数据

    正如 Ian Murdock 在自由软件界是名震一方的领袖,在现实生活中仍然难逃被轻易击毙的命运一样,在隐私保护的领域,很多事情已经超越了网络技术本身的范畴。信息使人获利,给人带来危险,都是因为信息映射着现实世界。

    大隐隐于市

    隐藏自己的身份,比公开身份然后将自身安全押在加密上明智很多。毕竟,加密防君子,混淆防小人。细究前面说到的信息,也可以略作分类。

    • 可以选择不发布的匿名信息
    • 可以选择不发布的实名信息
    • 必须发布的实名信息

    第一种情况某种意义上也是一种理想状态,除非使用了繁复可靠的安全措施,同时监视者不愿投入过大的资源破解。加上随处可见的手机号绑定提示,多数时候网络账号对应的情况是第二种。不过这当然不是非黑即白的两个极端,如果真的停用一切网络服务,日常生活会面临极大的不便,朋友也会觉得自己无趣。如果你足够无聊,甚至可以打造多个账号来学习明星打造自己的「人设」。保证安全的关键在于谨记「凡在网上的,必留下痕迹,不可撤销」。

    第三种情况就很复杂了。日常办证办卡总会留下点记录,法律不健全的情况下,隐私无法确保安全。令人不安的是,这类信息不像网络账号可以自由创建多个,而是和人身份强绑定的。老司机会告诉你开房时用护照,戴口罩,用现金。不过更重要的,可能是认真评估这类信息泄漏带来的负面后果。比如接到诈骗电话,比如被人将真实身份和网络账号关联起来公开信息……就像防御 XSS 攻击。许多人都喜欢给自己取一个独一无二的特殊 ID 作为各类网络账号的用户名来标识自己,这很棒,只要它不关联到现实身份,或者被人连线挖出某些信息时不会觉得尴尬。

    信任

    熟悉 TLS 协议的朋友都清楚证书机制是如何工作的,一条信任链必须存在一个用户绝对信任的起点。如果这个起点不值得信任,那一切安全都无从谈起。对于隐私或数据,安全也建立在一层层信任机制之上。一个 iPhone 用户需要信任苹果公司不会无底线出卖其数据。请注意,这跟 iCloud 无关。如果往阴谋论方向深究,iPhone 完全有可能不按照用户设定,悄悄地发送数据到苹果服务器上。这一切建立在对苹果公司的信任上。就像一个人如果不信任银行,也不会去银行开户存钱。(除非他无从选择)对不同公司不同的信任度,决定了何种私密等级的信息可以被存放在这家公司的设备或服务中。可怕的其实是,这些公司事实上的可信赖程度比大众直觉上的信任度低。FSF 的观点看似极端,可往往最终都是对的。

    谨慎信任,保持匿名,时刻加密。

    延伸材料

  • 记2017年开源年会

    说起来开源年会过去都已经两周了。写参加某项活动的经历似乎像是小学生写作文,不过还是有必要的,毕竟这也算一个自己一直以来向往的事情。每个人对于「开源」的认知都是不同的,这类活动也是,各取所需。说白了,也可以用这种观点来看待上课。现在的课程,一类已会,一类不会,到头来还是没什么长进。

    会议的流程和其他技术会议类似,签到入场后门口一排赞助商在为自己的产品打广告。场内演讲的时间很紧凑。有些内容感兴趣,有些内容又有点鸡肋。噢,有送可乐的,很实在。

    上午第一场报告是一个国内学者做的,大致讲了一下开源和云计算相关方面的趋势和问题。没有什么亮点,或者说没有本人的关注点,所以印象不深刻了。后面是 GNOME 基金会主席的演讲,跟技术无关,说了一下在当今桌面软件依然具有独到的重要性,以及鼓励大家能够多参与到像 GNOME 这样的项目当中去。当然,我是一个 KDE 爱好者。(如果就 Linux 的这群桌面系统而言)

    我个人对桌面软件开发知之甚少,因此不太清楚这样的桌面系统的组件的复杂程度和难度。有时候只是单纯地觉得,作为自由软件社区的开发者,靠自发组织是难以维系像桌面软件、浏览器这类众口难调,又期待用户体验的程序了。不同的人看待开源社区自然有不同的视角。除了开源软件本身蕴含的技术价值,开源软件的开发模式本身已经成为一种神奇的现象。传统意义上认为的需要精密严格管理才能完成的项目居然在如此松散的组织条件下也能做到,还能显得生机勃勃。当然,现在的开源生态,包括不同项目各自使用的商业模式,恐怕也是 Richard Stallman 当年未曾想到的。有人将开源软件视作道德标准,有人以质量作为开源软件的核心优势,有人则把开源当作能更好完成项目的开发模型。不得不说这就是开源世界的魅力。其实开源并无什么必然准则,开放扩张自然带来无穷的生命力。

    后面一位演讲者也来自外国,是 Apache 基金会的前副总裁 Niclas Hadhman 先生。这位老哥如果生在中国,那就是个不折不扣的愤怒青年。一上来先解释说自己不想讲一些重复的东西,大家有兴趣可以去看之前的讲稿。中间介绍了 Apache 基金会的一些项目。(说起来 Apache 基金会的项目除了起初的 HTTP 服务器之外好像都是 Java 世界的产物。)后面指出了中国人或者中国开发者在参与开源项目过程中的一些问题,或者说和国外迥然不同的地方。其一是中国人甚好微信,选择用微信来完成一切事情。我个人对于微信的看法其实是相对负面的。在早期年轻人用微信更多是为了好玩,有人会将自己的微信号公开在网上。而现在微信成了社交关系中不可避免的纽带,也影响到了在中国的外国人。

    微信像是一种工作关系的延伸,特别是在工作生活界限不明确的环境当中。我不喜欢微信这个软件本身的很多功能设计,尽管它和它的爱好者们喜欢以「简洁」等词标榜自己。社交软件背后一定是承载着特别的年龄人群的。即使是像微信或者 FaceBook 这样全民级别的软件,内部也隐式地根据不同的年龄层分化出相对离散的用户群,并且产生着独立的社交文化。话说回来,微信的确是个不错的支付软件。尽管在可以用银行卡的地方,我觉得不用输密码的闪付更方便一点。(如果他们不执意让我签字的话)微信的很多问题在于,它是一个运转良好的通讯软件,但它与 QQ 都只是腾讯生态下的核心平台而已。它们是应用,而不是一个开放的协议。微信不让我导出聊天记录,新进群看不到之前发言,微信不能接入机器人,更不支持插件。(不久前腾讯似乎以反垃圾信息为由关闭了微信网页版,真实目的不明,不过我想在网页版下可以很方便地利用浏览器插件实现端到端加密,尽管目前尚未耳闻有人这样做)

    后面的批评包括了对于中国文化的常见看法。比如中国人笃信权威,但是开源社区里没有一个绝对权威。不过我认为这其实是来自于这些中国参与者对开源文化的不了解。日常生活中的会议目的是求同存异,开会各方最后的目的是达成共识然后开始某项进程。而开源社区很多时候不是这样,去参与一个项目的目的很可能就是 Just for fun 或者使自己用着更方便一点。在这种背景下,达成共识是好事但不是必需之事。(苹果毁掉 KHTML 这样的事是道德范畴,个人做不到也不太可能去做)所以无法达成共识的时候,那就分裂。开源社区并没有限制个人在遵守许可证条款前提下分裂项目的权利。如果自用,那无所谓;如果有不同意见,等待自然选择即可。他还提到了开源社区中英语中心的观点,认为这不利于开源的发展。(抱歉,这一点记忆不是很清晰,可能不太准确)也有人认为在 GitHub 上用中文交流可耻。我个人的观点没有那么激进,也不清楚 GitHub 有没有相关的限制。Ruby 社区的邮件列表中也专门有日语板块。对于语言问题不妨用更实用主义的态度,如果一个软件的主要用户和贡献者都是中文使用者,那用中文交流甚至注释都并无大碍。如果项目涉及到来自多个文化的参与者,至少为交流内容提供一份英文副本是有助于它的传播的。这是一个沟通成本的问题,和中文本身没关系。请不要将中文附带上额外的政治意义,也不要因为国内不负责任的译本盛行而认为中文缺乏表达力。坦白讲,以许多程序员(也包括我自己)的语文素养,完全没有资格质疑中文和其他语言在表达能力上的差距。

    演讲中还有一点,就是来自中国的 Apache 开源项目多是由企业捐赠,而不是个人爱好者的开发结果。有朋友表示是中国程序员普遍压力比较大的原因。我认同这一点。不过还有一个原因是之前国内还没有形成一种程序员的黑客文化。现在看来已经好很多了。当然,一些基础设施类的项目还少有国内独立程序员的参与,这大概也跟技术积淀有关。况且国外许多有价值的开源项目都出自大学之手,而国内大学的教师普遍在干什么,明白了吧?

    不得不说 Niclas 真是一个有意思的人。后面还有一场短的演讲题目叫「软件宅神进化史」。指出现在软件开发走错了路,没有试图用更少更好的人来解决问题。印象最深刻的有两点。一是他强调一个团队人数一定要小,五个人的团队开发效率几乎大于五十个人的团队。二是有听众提问,人工智能是否会取代程序员的工作,他说「大概是会的,不过离今天还很远,至少二十年吧,而那个时候我已经不在了,所以我并不想考虑这些问题」。实习、软件工程课本和学校项目的经历已经有力地说明了第一点的内容。过多的人员配置真的会带来极大的负担和沟通成本。学校的老师恐怕也只是出于偷懒的心态要求诸如八人或者十人的队伍。

    下午有四个分会场,看个人兴趣跑来跑去。当然比较困了,听有些演讲的时候都睡着了。挑一些有印象的来说吧。每个分会场有各自的主题,大致是关于明星项目、草根项目、开源社区、开源治理。开源社区那一场的主持人是一位来自台湾现在在华为的工程师。开场的演讲也由他带来,聊了聊两岸三地开源社区的发展和不同,不过主要是关于台湾。台湾的政府部门也支持过开源项目,不过后来终止了。印象深刻的是在台湾,开源已经发展到超越软件从业者本身的一个事业,转变成一种社会运动,典型的例子如台湾社区发起的 g0v 零时政府项目。这才是体现了开源本质精神的项目。一则推动信息开放,二则使人人可参与。每一个人,都需要对信息有更多的了解,不仅仅是作为一个使用者的角色。屡屡爆出的信息安全事件(比如「永恒之蓝」)和曝光出来的地下产业,都是证明。

    开源社区分会场后面有一场姜军带来的有关 RubyConf China­ 的演讲。很实用主义,提到了筹办一个技术会议过程中的种种花费和问题。晚宴时坐在姜军旁边聊了几句,才知道他是大学肄业。明年的 Ruby 大会已经向松本行弘和 Aaron Patterson 发出了邀请函,在上海举办。挺期待,虽然那个时候应该不是 Ruby 程序员了。

    另外两场演讲,一是如何用开源盈利,二是关于开源运用中拿来主义的心态。内容都比较中规中矩。开源事业能发展到今天,的确也不只是个人开发者的义务劳动能够涵盖的成果,许多商业公司也为开源项目贡献了代码,不少开源项目也找到了盈利模式从而活了下去。典型的例子包括 MySQL 的双重授权,RedHat 的服务收费,一些软件包提供商业的高级版,Google 靠其他业务变现等等。讲者强调了两点:做开源不要忌讳谈钱;所有盈利模式的基础都是用户数量。另一位讲者从文化的角度分析了企业不愿意向上游贡献,不愿意付出资源到开源事业的原因。个人看来,这也跟环境有关。大环境不尊重知识,法律对版权的保护也不到位,外行指导内行,就是这样的结果。不过在可见的未来,这是可以改变的。

    后面有一位法律人士在谈 GPL 的传染性问题。很遗憾没有听到。在今天,GPL 协议几乎快到了「人人喊打」的程度,也是令人唏嘘。如果我写了一个自己用的小程序,我会选择用 GPL 发布,因为它代表了自由软件的纯正价值观。但是如果我的目的不是自己日常使用,而是想推广它,恐怕就会考虑 Apache、MIT 或者 BSD 一类宽松的许可证了。作为个人实质上很难去猜测 GPL 或者自由软件基金会的未来,因为 GNU 和 GPL 项目在整个开源世界里所占的比例越来越小,受关注也越来越少。但是没有人能够否认 GNU 带来的工具链在整个体系中的核心地位,大家也几乎都承认是 GPL 保护了 Linux 这么多年来的顺利发展。自由软件的概念也慢慢被开源软件所取代。在软件世界发生的事情不禁使人联想到现实世界:理想主义者作为开拓者推翻了旧的世界,但最后却是由资本和权力接管了世界。靠义务劳动和热情是无法完成如此巨大的事业的。现时开源软件已经是任何软件开发者和公司包括微软都避不开的一个话题了。我们有足够的理由相信开源世界明天会变得更好。然而现实不仅仅是源代码。新的科技引出了新的问题,个人隐私比以往任何一个时代都危险。庆幸还有 Richard Stallman 这样的义士继续为自由与开放而奔走呼号。

    最后的晚宴不错,第一次吃黑胡椒牛排炒年糕。下午的茶歇也很舒服,对蛋糕的认识有了改观。晚上的活动结束之后,遇到一个用友的工程师,扫地僧一样的角色。他对我说了一句话。

    「你今天对开源社区付出的,明天社区会十倍、百倍地回报于你。」

    我想是的。

  • 我为什么讨厌狼人杀?

    昨天早上在知乎上看见有朋友分享这个「你为什么讨厌狼人杀」的问题。点进去之后我非常惊讶——居然在政治、社会话题之外还有这样的让我感觉「解气」的问答。说大家是落井下石也好,不会玩也好,总之无法否认,这个风靡街头巷尾的游戏,是像任何一个明星一样,有不少人对此反感的。

    我记得我最开始接触这个游戏的时候还是在初中。那时对杀人游戏和狼人傻傻分不清,印象里反正是那些认识很多朋友的外向同学出去玩的标配。玩得不多,「天黑请闭眼」还是记得住的。一直到刚上大学那会,每次玩这个游戏似乎大家都傻乎乎的,如果是陌生人那就随便票,如果是认识的一起玩,当然就是把大家最爱开玩笑的对象投死了。

    其实我真的也不知道狼人游戏是怎么在不长的时间内转变成现在这个样子的,就像我不知道如今微博上一个小有名气的明星转发评论赞数都能上万了一样。狼人和狼人杀有什么区别我不清楚,不过我猜想这名字或多或少都得跟三国杀有关系。说起来,随便一家小的桌游吧藏有的游戏都能摆满一整个柜子,可是大多数人出去玩能想到的恐怕还真只有三国杀和狼人。UNO的话,其实真不太想跟不熟的人玩这个。跟前两者比起来,这个游戏的标准规则存在感实在是太弱,变种又太多,有冲突就挺尴尬的。不过好在它节奏快,而且各自为战,运气成分强,玩起来还是算轻松愉快的。

    回到原题。头一次接触这种「高端」的狼人游戏是去年夏天给一个朋友送行的时候。一帮还算熟的人去桌游吧玩狼人,我当时倒是爽快地答应了。第一把如往常,大家一开始就把东道主给票死了。第二把的时候就被叫出去和陌生的老玩家一起玩。头一回见着这样的分析我是很震撼的,还在一边听一边掏出手机查那些接头暗号一样的术语到底是什么意思。第一轮,很自然的,因为我都不认识,所以随便挑了一个指。结果就是这样随便一指,在第二轮搞得高玩们分析了半天,还纳闷为什么大多数人都喜欢装预言家。很有趣,但是也挺无聊的。那天晚上后来又经历了几次随便过然后被票,也有分析了一波带了下节奏的。回去之后跟其他同学聊天的时候把那天晚上的见闻吹嘘了一番,毕竟我的确是震惊不已。后来我再也没有玩过狼人杀,也的确不想再玩。

    其实在我看来重点不在于什么黑话多不多。仔细分析一下可以发现狼人杀是一个「推进感」很强的游戏。这个词是我自己采用的。推进感是什么?狼人杀可以说不需要什么硬件设施,就几张牌抽身份而已,要搬上网络不能更简单,基本上属于一个纯语言类的游戏。既然没有硬件设施,游戏的进行全靠每个玩家的发言作为载体。如果每个玩家都不说两句话,这个游戏几乎就无法进行。这样的逆向淘汰也就是早期狼人深受交友爱好者喜爱的原因,因为他们更爱说话,没有太多顾忌,尽管那时还没有这么多套路。如果你是一个喜欢发言并且掌控节奏的人,这个游戏当然能带给你别致的体验。但是对另一部分玩家来说,这就是巨大的压力——为了游戏的正常进行,你不得不发言,强行分析。和日常辩论不一样的是,这样的发言充斥着谎话。我当然知道游戏中的谎言无法代表玩家现实生活中的人品,但是不能否认,有不少人无法这样面不改色地说谎。就我个人的感受而言,这个游戏过于紧迫,不像我在玩游戏,而像是我在被这个游戏和其他玩家玩。

    竞技类游戏也是这样吗?大概是的。不过如果你不是职业玩家的话,完全可以娱乐一点。而且嘴炮水平和实际能力完全不挂钩,有时候不会让人那么反感……而狼人杀,现在大有一种「图个乐呵」的玩家被所谓的高手绑架的趋势。更令人不悦的是,乐在其中的人一方面在游戏过程中强调它的竞技性,反感那些不认真玩的玩家或者是坑队友的新手;另一方面又把这个游戏和社交挂钩,默认所有人都应该像自己一样充满节奏感。如果我和人出去玩的时候拒绝一起玩狼人,恐怕是会被扣上「不合群」的帽子的。电脑游戏的话,至少别人拉我去上网的时候我说我不会玩英雄联盟玩玩其他的好像也不是什么严重的问题。

    长大了真的会发现,人和人之间的价值观可能会有巨大的差异。于我个人而言,我真的不太喜欢游戏过程中的竞技性。初中一开始我也和同学一起打篮球,后来的Dota什么的我也玩过。为什么总是进行不下去呢?配合少也好,水平次也好,玩游戏真的就图个热闹,这是价值观的问题。所以我选择不参与,不干扰好胜玩家的正常游戏。我认为这是一种尊重。我不玩狼人杀,我喜欢被动一点硬件多一点的游戏,我会对没玩过的桌游感兴趣。我说了许多我不喜欢狼人杀这个游戏的理由,也许在读这篇文的人一条都不认同,但你没有资格反驳我。因为我没有干扰你的游戏,我也不会因为一个人的爱好就随便下结论。我们当然也可以成为很好的朋友,但未必就要用这种令我不悦的方式。

    我很怀念高中的时候周五偷偷玩七大奇迹把科技树点到底,也怀念大富翁下得几千上万生死一线间,一起玩UNO被加了十六张大家哄堂笑我也很激动。回头看也许这才是面对面游戏本质的乐趣。

    可我不喜欢狼人杀。准确地说,是这个以社交名义绑架你我的狼人杀。

  • 关于大学老师上课的一些看法

    好像很长的时间都没有好好写过文章了,也不知道放在这里会不会有人发现。之前约定自己,每天或者每周更新这个博客,可惜这件事到现在并没有做到。说实话,其实就是自己懒,未能规划好时间。说到这里,的确是有点羡慕那些平日上课按部就班的同学了。也许他们的实际「水平」不如另外一些人,不过每天跟着老师的进度走,自己完成作业,也按时交,不怕点名,感觉大概是很棒的。这个学期期末的时候要交数据结构的课程设计,那些作业本可以很早以前就写完然后上交的,然而总是觉得这不对那不对。题目只要求用命令行的界面,但是怎么看怎么觉得有点不太能上台面;但是用GUI做吧,用什么库呢?好像也不好看,带个东西也臃肿得很,要求叫可执行文件,还得麻烦一下用Windows的同学。所以就一直拖着。一直到Deadline前,才意识到这个东西实在不能再拖下去了,于是就赶紧三下五除二赶好。你别说,赶作业那段时间虽然时间紧,但是感觉还挺好的。之前的C语言大作业、Cocos的游戏作业,都是这样的,有别人推着走的感觉会很稳。不像自己学东西,没有什么自制力,说好的自己要写编译器,拖拖延延到现在还是没个头。

    不过呢,这次去北京参加大型主机的比赛,整个过程中开了不少的会,也在北京玩了一把,收获确实是不少的。每天或者每几天review一次的感觉相当麻烦,不过真的可以保证某个项目的稳定推进。之前想的是,对自己制定一个完整的规划,自己照着这个规划来,每一步完成一个什么。但是一个可行的规划就必须要求制订者对这个项目和工作效率有基本的认识,多数时候这就是意味着丰富的经验。对于个人学习的项目来说,这种做法多半是不可行的。所以不如「走一步看一步」,但是要求一定得强硬,这比较适合假期一个人的时候学习。又说回这个编译器的想法,该写的是一定要写的。可以不忙着设计语言,就照着C11的标准来就行。实现语言的话,可以用Ruby可以用C++,乃至OCaml。我还计划大三的时候开门课讲编译原理呢,估计那时候就真的得用C++来搞了。说来也是惭愧,这学期选了门编译原理,结果好像就没听几次课(老师懂肯定是懂的,但是上课真的无比蛋疼),下周就要考试了还不知道做了多少准备。

    说到老师的授课方式,好像进入了这篇文章想要说的正题。讲真,第一次在这个博客上如此激动的想写那么一点东西下来。其实回想自己大学生活的过程,可以很明显地感觉到对自己的要求在一步一步地变低。说大一的时候不谙世事也好,说是因为想转专业积累绩点也好,那个时候听课的确是认真的。也不知道是不是我的错觉,老师也比现在要走心一点。第一节西方哲学史的课堂上,韩潮老师看着几个迟到的同学说:「你们现在才大一,不要这么早就像高年级的同学一样了啊。」对他的课印象比较模糊,因为我总是想睡着,可就像高中的物理课一样,每次真的要倒下去的时候,脖子又会坚强地抬起来。这样往复,还真的能坚持几十分钟。政治课也是认真听的,下课一没事就去图书馆看看书。那个时候心里没底,但生活却是最规律的。

    来软件了之后,直到大二,我似乎明白了一个道理:大部分专业都会把讲课最令人舒服的老师放到大一来开课,可能是让大家至少不要「堕落」得那么快吧。都说物理像高数一样,不过那时候我的高数课上得还是挺开心的,困归困,基本都不玩手机,按时交作业,身边也没有同学可以抄。那时候想的是,大学的学习也不过如此嘛。哎,人就不能过早下定论。

    怎么讲呢,之前我也是从来没想过挂科的。就连物理上,也是期末抢救了一番之后,成功保住了一个及格。虽然是同一个老师,可物理下就没有那么幸运了。大概是我太水吧,真的一点不会,平时也不想听。最后期末复习看书的时候,真的有一种想杀人的感觉——我真的,从小到大都没有这样窘迫过,为什么一定要这么为难我?为什么?恶性循环嘛,所以到今天,我的物理挂了三次。第一次挂科的感觉真的是很难受的。在同济,挂科的人每学期一定不会少,找老师求情的也不会少,但是像我一样把自己的光荣事迹放到校内公共平台上,同时质疑物理课开课必要性的,恐怕真的没几家了。说真的,在网上狠狠同别人吵了一架之后,物理对我造成的伤害反而没那么大了。我的概率论好像也挂了。看来到这个时候,就是我的问题了。或者说,早就习惯了人文的复习方式,一下这样「动真格」,是的确不适应。

    好像又说远了。各种有大学生的论坛里,吐槽授课教师上课水平低的绝对不是一两例了。可不知道有一些人到底是真糊涂还是装糊涂,强行立了一个逻辑,说「学生水平还不如老师,所以没资格评判老师的上课水平」。这就让我想起了高中时候的某同学。我说,我觉得汪东城长得不帅啊。他讲,「你自己照照镜子看看你有他帅么」。事实上学生当然是有理由指出老师上课的不足之处的,因为学生就是老师授课的对象,恐怕最有资格来做评教这件事。可惜一个老师来上某门专业课未必是出于普及知识拯救学生的信念,而是无奈受迫所致。这就导致许多同学毕业以后,不记得自己上过多少有干货的专业课,反而对某些选修课文采飞扬的老师印象颇深。你当然可以说,真知都是枯燥的,仿佛老祖宗讲的良药苦口一样。可为什么现在的不少药,外面都得加个糖衣吗?复杂、系统的知识当然需要花费大量的时间理解和练习,但是这绝对不是任课教师不认真备课,不认真思考课程改革方案的理由。当然这不只是任课教师的锅,学院的思想僵化,领导独断专行,都是共同原因。不过在下确是认为这些不太受学生欢迎的老师,应该反思反思,自己有没有落实好这个「培养计划的最后一公里」的角色。

    知乎上跟教师有关的话题,尤其是大学教师的话题,我时常觉得不堪入目。大清亡了都一百多年了,女权主义都要成热点话题了,为什么众人对教师职业的看法还停留在那个时代?老旧的尊师观念、苏联式办学传统和官僚体制下僵化的大学培养制度共同诞生出了我国目前本科教育这个怪胎。很多人讨论问题的时候都是在想当然。同济有没有严格的老师?有。但是这些老师被学生骂到死了吗?没有,反而受一代代的学生尊重,每年选课的时候都会成为抢手货。因为这些老师的严格,不是目的,而是达成教学目标的一种手段。学生通过老师的严格,最后获得了真正的知识和成绩,所以才会反过来感谢老师。有些老师口口声声「其实你成绩怎么样和我有什么关系」,实质上就是在为自己的不负责任找借口。学生不听课,不代表你老师就可以瞎干了啊。

    但这个问题并不好解决。所以在可见的大学生活里面,学习这档子事情,还是只能靠自己。自觉是不一定靠得住的,正如一个人只能溺水而死,不能憋气而死一样,要适当创造一些「自己无力改变」的短时条件,比如上课不带手机和电脑。或许效果会好很多,至少我可以自己看书。

    所以还是想给下学期好好定个安排,我记得以前也定过,常变常新嘛。

    • 物理一定要过,否则以后的课程安排会受很大影响
    • 实现一个C语言编译器,后端生成nasm汇编代码,实在没时间用LLVM也可以,要在今年GSoC之前完成
    • 至少把《经济学的思维方式》《纳粹德国:一部新的历史》和《死亡》读完
    • 下学期上课尽量不带手机和电脑,每次上课坐前1/3排
    • 买一对哑铃,或者每周去健身房三次
    • 读《Ruby源码剖析》这本书,尽量向今年GSoC的Ruby项目靠拢,大三可能没那么多机会了
    • 有空的话,深入学习OCaml,看一下《近世代数》
    • 去一个江浙沪之外的地方旅游
    • 每周至少更新一次这个博客

    一开始的语言可能有些激动,请原谅我这个人许多时候的自以为是。

    祝好,送一声迟到的新年快乐和早来的新春快乐。

  • Ruby on Rails开发入门手记

    上个月的博文里说过,由于要开发同济权益的二手书管理系统,所以用PHP写过这样的代码。第一个版本是纯PHP撸成的,没有用到任何的外部框架,路由也是纯手写.htaccess文件实现的,一共就四个文件,负责分发请求,最后交给处理数据库的文件来输出对应的JSON信息。现在看来,这大概叫强行RESTful吧,哈哈。话说回来呢,当时写的时候,甚至还有点基本的ORM思想,不过觉得太麻烦就没着手完成这个。后来才知道这个模型有个高大上的英文缩写叫做ORM。当然,这个版本的代码,模块性一点都不好。五百多行,基本没法维护。

    后来寒假的时候,确实是觉得这个太不行了,需要找个框架重写,顺便学习一个。用哪个框架呢?知乎搜索了一下,就用CodeIgniter吧,相对轻量,又确实有用。这个框架是MVC架构的,但是也没有实现完整的关系对象模型,仅仅是封装了方便的数据库查询器而已。这个版本,自我感觉代码结构好多了,不过好像和前端交互的API还是没有改。

    到了四月初的时候,听说软件学院要搞一个Android应用开发比赛,所以呢,后端可能还得改。这次大家坐在一起开了个会把需求相对完整地定了下来。不过,还没等得及下手,一切又变了。应用还没开始做呢,就又变卦了。这次是得改变架构,网页和后台不分离。大概就剩一个月的时间了。怎么搞呢?用Ruby on Rails吧。听说,这玩意很快啊。

    要说之前,Ruby这个语言我就没有碰过。虽然它和Python我都不熟,不过其实还是用Python相对多一点。但是不行啊,硬着头皮也得上。所幸Rails的文档写得确实不错,我做这个网站的过程中很大程度参考了Ruby on Rails的中文指南。如果有朋友对Rails这个框架感兴趣,非常推荐这一系列文档。同时,也听说Ruby China在国内的技术社区里面是最有氛围的一个了。

    Rails无非也就像是其他MVC的Web框架一样,在目录下通过命令生成一个新项目,然后运行服务器,打开看到一个正确运行的页面。这就标志着我们的开发工作正式开始了。当然由于Rails这个框架比较复杂,生成的代码也比较多,所以新建一个控制器还不能像CodeIgniter这种框架一样直接手动新建个controller文件就好了,需要用到generate命令。敲下命令之后,发现Rails确实爆炸,自动帮我生成了RESTful风格的路由和一堆代码。

    由于朝RESTful看齐的特点,直接在Rails的路由文件routes.rb里像这样写:

    Rails.application.routes.draw do
    
      resources :books
      resources :users
      resources :appointments
    
      root 'welcome#index'
    end

    寥寥几行代码,Rails就帮我们生成好了书、用户、预约三种资源的所有路由,已经定义了网站根目录对应的控制器方法。

    说到生成,Rails还有一个更厉害的东西,叫做「脚手架」。利用它,可以一键生成控制器、视图和模型的代码。虽说这些代码直接拿来用不太现实,不过也可以作为初学者学习的重要参考。

    Rails的脚手架用法如下:

    rails g scaffold books title:string price:decimal

    这样就快速创建了一个叫做books的资源,包含title和price两个属性。脚手架为我们快速创建了路由、控制器、视图、模型,甚至测试文件。脚手架生成的代码虽然完整,但是往往不能完全满足我们的需求,我们也未必喜欢它给我们强加上的一堆网页样式。我们可以基于脚手架的代码来修改,不过更多的时候只是用它当作例子来学习,看看标准的Rails代码是个什么样子。

    Rails严格区分了资源的单复数。一个资源在作为Model层的时候是单数的,比如Book;而在View层和Controller层的时候是复数的,比如books_controller.打开Rails为我们创建的这个controller,我们可以发现七种RESTful的操作已经预先定义好了,并且自带html和json两种模式。很迷的是在许多方法里只有一行语句甚至没有语句,那么Rails是怎么渲染View的呢?

    实际上,Rails会自动找到与controller里的方法同名的模版文件(Rails默认用的是erb)然后渲染。当然有些方法是不需要HTML模版的,比如POST、PUT这些HTTP请求使用的controller就不需要特别的模版,用得更多的是重定向。当然,我们可以手动选择要渲染的模板。即使用render方法。

    Rails的模板是怎么写的呢?因为Ruby语言不靠缩进来标记语法层次,所以Ruby可以轻松地嵌入到HTML模板文件当中,这一点比不得不自造模板语言的Django高到不知哪里去了。这类模板也就是所谓的erb。Rails可以在设置里选用其他的模板,比如haml乃至markdown。

    被渲染的erb模板能够正确地加载调用它的控制器里的所有实例变量。什么意思?比如像这样:

    # controllers/books_controller.rb
    class BooksController
      def index
        @time = Time.now
      end
    end
    
    # views/books/index.html.erb
    <p>Current time is <= @time %>.</p>

    所谓的带@符号的变量是Ruby的一种语法,称为实例变量,表示类的数据成员。如果我们去掉这里的@符号,把time定义成局部变量的话,模板渲染的时候就会报错了。

    我们把Rails的视图层和控制器都极简地介绍了一下。尽管没说几句话,但是已经基本可以构建一个可以运行的网站了。这都要归功于Rails的良好封装和Ruby语言的自由度。现在我们来看看之前被忽略掉的Model层。实际上,Rails更加鼓励把更多的逻辑写到模型层里面。(Rails还需要我们为controller写什么代码吗?)对于书这种资源,我们也能在models目录下发现对应的代码文件book.rb,注意到book用的是单数。

    class Book < ActiveRecord::Base
    end

    咦,这个Book里什么代码也没有,就一个空类嘛,有什么用?

    别急,如果我们在Controller里面想要查询关于Book的信息,非常简单:

    def get
      @book = Book.find_by_id params[:id]
    end

    有趣,这个find_by_id是哪来的?噢,忘说了,Ruby里面方法调用跟Perl一样不需要括号噢。

    哈哈,这就是Ruby语言的威力,它可以把字符串和代码相互转换,并且可以在运行时动态定义新的方法,甚至覆盖旧的方法。Ruby看到我们find_by_id这个方法时,不会急着报错,而是去寻找有没有「当方法找不到时的解决方案」,而Rails在这里为我们提供了一个。它会去数据库的结构里查找id这个字段,然后返回给我们想要的结果。也就是说,如果我们的Book表里新增了一个字段叫ISBN,那么我们可以直接find_by_isbn了,什么也不用改。

    这只是Rails的数据库子包ActiveRecord强大功能的冰山一角。另一个值得一提的地方是它的关联机制。比如这样:

    class Article < ActiveRecord::Base
      has_many :comments
    end
    
    # 注意单复数
    class Comment < ActiveRecord::Base
      belongs_to :article
    end
    
    # Rails会去寻找Comment表里article_id这个字段
    some_article = Article.take
    some_article.comments # 我们已经得到了此文章对应的所有评论

    这里对article_id这个字段名的假定体现了Rails的一个重要原则:「约定大于配置」。相比于Java繁复的各种XML配置文件,不知道Rails的这种风格有没有让你觉得清爽呢?

    拖了六个月,说了这么多,其实也就是Rails最基本的一点点。不过足以让许多人认识到Rails框架和Ruby语言的魔力了。整个Ruby社区很有趣的一点就是,从Rails的插件到各个方面的库,作者都会以这个库的使用方式「看起来像英语」为荣,比如:

    every 3.hours do
      sleep 2.minutes
    end

    得益于独特的语言文化和语言的强大能力,这样的DSL在整个Ruby社区到处都是!

    文章很短,不过希望你能从中感受到用Ruby编程的魅力。就像DHH说的,成为百万富翁以后才发现,开兰博基尼还不如写Rails来得快乐。(虽然我没开过)

    以下有两个页面可供想学习Ruby on Rails的你参考:

  • 不受待见的程序员

    程序员正在遭受来自这个社会的,同对医生一样的误解。为什么大众会误解医生呢?或者说,为什么医生与患者间的关系开始变糟了呢?行医救人的职业古已有之,尽管那时尚无「白衣天使」这种搪塞个人追求的名字。然而工业时代来袭,全世界的居民都接受了经过塑造的现代生活方式、谋生手段,且几百年而不绝,这种改变仍在快速进行之中。物资丰富、日新月异的年代,每个人都有权获得更好的生活。为了区分这个先后缓急,才有了资本、技术、劳动作为收入分配的依据。医术自古即是一门技术,但那时技术还算不得资本,更何况是「士农工商」的年代?

    现今的人不能理解社会系统、国家机器运作的复杂性和脆弱性,他们仍习惯于以小农时代的思维模式打量这个世界的运转。也正由于此我们能够时常看到身边人关于社会管理、国际关系等的的种种呓语。正如和你大谈高层秘辛的出租车司机不必深究汽车原理便可参与营运一样,现代社会允许其成员未曾窥过其逻辑即可自由地享受生活。简言之,现时的医生早已不同于古代,就医过程的参与者也不仅是患者与医者双方。用想当然的逻辑思考规整化的医疗体系,自然是会出问题的。

    程序员就更可怜了。如果说医者的职业目的大众还算心中有数的话,对程序员,就真算一片空白了。「开发软件」?软件是什么?即使是日常用到软件的人,也不清楚这些司空见惯的程序的复杂程度(「2000块给我做个淘宝」)——从这个角度讲,社会和软件倒有相似之处。须知,大多数人对职业的认识几乎完全是从个人经验出发的——比如律师就是耍嘴皮子,设计就是随便画画。在建筑行业整体遭冷的今天,仍有不少的重庆人相信搞桥梁是最好的职业之一,因为他们一辈子见得最多的就是桥。更何况,在风口可以吹猪上天的今天,程序员引来了诸多行业的羡慕与嫉妒。又有大批对计算机几无敬畏与热爱的人入行以工程师自居。这些拿着半吊子水平的Java就大谈而谈各路本质论的人继续伤害着程序员在大众眼中的观感,误解自此不可避免。

    说「这个世界还未能体会程序员带来的美和智慧」有些过了。对于我这个不爱车的人来说,我也没法感受搞汽车的人乐趣在哪。不过我清楚汽车是什么,做什么,大概是个什么样子。我也明白现今驾驶、乘坐的体验是上百年一代代工程师的努力带来的。可……把「汽车」换成「软件」呢?这确是教育的问题。今天又越来越多的人把杨永信的网瘾治疗当作是一个笑话,但不要忘了在2016年还有大量的人想在互联网上再来一次卢德运动。可悲又可笑。

    未来当然会有越来越多的非程序员学会编程并用以解决自己领域的问题,也当然会有程序员丢掉饭碗,正如原来的电话接线员。实际上,如果现在的程序员穿越回二三十年前:「不会手写键盘驱动来接受输入,你也配叫会编程?」技术的进步是为了解放人类,使其能考虑更高层次的问题。国内僵化落后的计算机普及教育,却会使更多的外行视编程为洪水猛兽,也无法正式程序员这个职业。

    做一个程序员有哪里好羞耻的呢?在「劳心者治人而劳力者治于人」的文化环境中,程序员的火热本身就是对「读书无用论」的重击。我很期待在未来看到工作到六七十岁的老程序员,那是社会理念更进步的表现。想一想,还有多少专业的人有资格以纯粹的兴趣看待专业问题呢,并拥有如此低的创造成本呢?

    全世界程序员,联合起来!

  • cJSON源码分析-实现

    好了好了,这篇文章拖了真久啊,活活三个月。不过之前承诺要经常更新博客的,所以先把这个烂尾的坑给填了。

    之前提到了cJSON这个C语言写成的JSON解析库的接口,也就是头文件里的内容。这一次我们来分析一下实现。上车吧。

    还是按照源代码的逻辑走。我们发现cJSON最开始有一个全局的字符指针ep,以及一个用以返回ep的函数。可以看出,这个ep是用来存储错误信息的。这种实现是C语言的常用手法,即把一些状态用static的方式隐藏在单个文件中,并实现一些函数当作接口。

    static const char *ep;
    const char *cJSON_GetErrorPtr(void) {return ep;}

    下面是一个大小写无关的字符串比较函数。看后面的源码可以发现这个函数用在了JSON对象名的查找当中。实现没有什么特别的难点,所以就不提了。顺便说一句,在C++里要实现这个大小写无关比较,可以用STL里的一个方法叫做lexicographical_compare,搭配lambda表达式可以轻松达到目的。

    作者把负责动态内存分配和释放的函数直接「填」成了标准库的malloc和free,像C++的allocator一样。如果担心内存碎片的问题,可以自己再当个二道贩子,实现一个内存池,不过那有点背离本文的主题了。作者自己实现了一个版本的strdup,里面也是用这个用户可以更换的cJSON_malloc进行内存分配的。

    新建JSON和释放JSON的工作不难,前者就是内存分配的问题,后者就判断一下JSON的type,如果是基本类型就直接释放,如果是数组或者对象就递归删除(类似二叉树)。

    目前好戏来了。第一个函数是解析数的。写过词法分析器的就懂,这个用自动机很容易描述,不过这里实在是没有什么好的能在电脑上绘制自动机图的工具,所以用列表表示这个过程,看看应该能体会。

    1. 开头有负号吗?有就记下来,向前走。
    2. 第一个数字是0吗?是就前进,反正默认的结果都是0.
    3. 这是小数点以前的部分,一位一位地循环解析就好了,到第一个非数字的字符为止。
    4. 有点并且点后面有数字吗?有就把小数部分也解析了加上去。
    5. 有E或者e吗?有就算10的幂次方。

    简单吧?我们接着往下走。看到一个…奇怪的函数。

    static int pow2gt (int x)
    {
        --x;
        x|=x>>1;
        x|=x>>2;
        x|=x>>4;
        x|=x>>8;
        x|=x>>16;
        return x+1;
    }

    看见这个函数心里大概会想——什么鬼?名字看不懂,内容也看不懂。唔,不过看在它参数和返回值都是简单的int类型,不妨写个小程序测试一下结果。(限于篇幅省略结果)跑完之后我们猜测,这个函数的目的大概是返回一个不小于x的2的整数次幂。(对2的整数次幂还不敏感吗?)那我们来根据代码验证一下。

    先略过这个减1的过程,看看位运算。我们假设整数的二进制形式从右向左,最后一个值为1的位是第n位,那么运算的过程是:

    1. 首轮,第n位右移1位,经过按位或运算,第n和n-1位(n右边那一位)确保为1.
    2. 第二轮,类似地,n和n-1都右移2位,所以n-3到n都是1.
    3. 第三轮,同样,此时n-7到n位都可以确保为1.
    4. 第四第五轮后,从1到n位都是1了。右移到16截止是因为这里的整数只有32位。

    最后往这n位连续的1上再加个1,就是1后n个0,即2^n了。起来这个过程有点故弄玄虚的意思,因为我们也可以用循环的方式来解决。不过这里作者巧妙利用了整数位数的限制,用五次位运算达成了对任意整数都有效的效果。为什么要减1呢?因为不减1再加回去的话,对一个已经是2^n的数进行运算会得到2^(n+1),不符合我们的预期。实际上,这样的位运算技巧在《高效算法的奥秘》和《深入理解计算机系统》中都有相关的阐述。

    这个函数有什么用呢?搜索一下就会发现。它只用在了一个地方,就是下面这个ensure函数。继续追踪可以发现,这个函数包括下面的update以及printbuffer这个结构体,都是用来存储缓冲区的。在缓冲区里面空间成2倍地扩大。不过我们的重点在字符串解析和内部的数据结构。

    到这里,我们回过头来整理一下思路。JSON的类型有6种,而操作又都有解析和输出两种。

    • null和bool,由于bool只有两种固定的值,所以对于这两者,输出和解析都是简单的strcmp、strcpy就可以。
    • string,解析本身难度不是太大,去掉两端引号中间的就是字符串内容。但是有两个(或者说就是一个)问题,一是要注意反斜杠’\’开头的转义字符,二是字符串涉及到utf16到utf8的转换。输出的话不是什么太大问题。
    • number的解析前面已经说过了,浮点数输出要考虑一下精度,IEEE754标准和utf8都是坑。
    • array和object都是递归解析。如果要按格式输出,缩进是一个问题。

    唔,好尴尬,写到这里突然不知道怎么继续了。在这里贴一下主要的解析函数parse_value的代码。

    /* Parser core - when encountering text, process appropriately. */
    static const char *parse_value(cJSON *item,const char *value)
    {
       if (!value)         return 0;   /* Fail on null. */
       if (!strncmp(value,"null",4))   { item->type=cJSON_NULL;  return value+4; }
       if (!strncmp(value,"false",5))  { item->type=cJSON_False; return value+5; }
       if (!strncmp(value,"true",4))   { item->type=cJSON_True; item-&gt;valueint=1;    return value+4; }
       if (*value=='\"')               { return parse_string(item,value); }
       if (*value=='-' || (*value>='0' && *value<='9')){ return parse_number(item,value); }
       if (*value=='[')                { return parse_array(item,value); }
       if (*value=='{')                { return parse_object(item,value); }
       ep=value;return 0;  /* failure. */
    }

    跟我前面的分类一样,很清楚了。至于空格,这个函数在每次被调用之前都会先调用一次skip函数,用来跳过空白的:

    /* Utility to jump whitespace and cr/lf */
    static const char *skip(const char *in)
    {
        while (in && *in && (unsigned char)*in<=32)
            in++;
        return in;
    }

    查看ASCII码表就可以知道32之前的基本都是不可见的控制字符或者空白,这里的条件判断真是简单粗暴。

    parse_array的过程已经说得比较清楚了,就是不断地跳过空格、读取逗号、再跳过空格、读取一个新的对象的循环……直到遇到反方括号。前面那个指针ep的用途也明白了,就是指向读取失败的地方。parse_object类似,只是每次循环还要插入一个读名字的过程。

    输出部分没有什么特别值得提的地方(其实是懒),要注意的就是输出array和object的时候需要控制一下缩进。总的来说,cJSON的代码逻辑就是这个样子。阅读这样「接地气」的代码,好处在于能够快速学到很多这门语言的最佳实践,但是繁杂的工程细节也会让人厌烦。好在大一些的项目往往在抽象上做得更好,方便我们抽丝剥茧,寻得新知。