读《三体》

《三体》是刘慈欣创作的系列长篇科幻小说,一共分三部分。

我在一年前就已经开始了阅读,读到第二部分的开头,就读不下去了。原因可能是场景转变太大,一下子没产生读第一部时的兴趣。

跟一个朋友交流,他也遇到了同样的问题。网上也有人读到放弃,但原因是太多物理知识不好理解。不过我觉得对于一本小说来说,不必要这么较真。

大约过了大半年,接近19年年终,我又拾起了这部小说,在江西的一段时间,彻底读完。

没有太体会到大部头,kindle 给人的感觉飘飘忽忽,对字数和厚度没有太大感觉。

那时坐在三楼的阳台,冬天的阳光有点暖,照得人很舒服,算是比较惬意的一段经历。

科幻小说

这是一本科幻小说,也就是科学幻想小说,它的基础是科学,所以有很多内容是真实的,在科学的基础上,开了一些脑洞。

还有一类小说叫文玄幻小说,这一类就不需要科学基础了。

之前饺子导演的一部动画片《哪吒》,哪吒从结界偷跑出去了,两个把门的小妖对话,”很玄幻”,”不科学”。对仗之工整,令人吧服。

小说发生的背景是宇宙,至于具体的故事概要,没有提及的必要,小说嘛。

优秀小说的一个标准

《得到》上有一个课程,叫做《跟着李新学编剧》,发刊词里讲到神作跟口水剧的区别,里面讲到一个标准我觉得很好,”一部神作可以让你搞懂一个行业的底层逻辑”。

这种底层逻辑是需要大量知识积累和大量精力的,从这一点也可以看出作者是否用心去写作或者创作,还是只是赚个流量敷衍了事。

通过阅读《三体》这部小说,一方面可以跟着作者对人类文明做一些更加深度的思考,同时也可以了解一些宇宙科学知识,是比较值得的一部小说。

通过这一条标准,很容易联想到《疯子在左,天才在右》这本书,不过不太好对其归类,只是做个参考比较吧。

对于人类科学的思考

一个确定的事实是,人类的科学是有局限的,而且是必然不完整的科学。

所以人类定义的时间才会从大爆炸开始,大爆炸之前是什么,没有人会知道。

小说里提到的黑域就是一个很有意思的概念,作者说最开始光的速度是无限的,但是我们可以通过一些技术来将某一区域的光速设置一个上限,甚至可以将某一个区域通过这种方式与外部宇宙隔离。

比如人类现在发现的光速是30万千米,但为什么是30万而不是40万或者50万,如果不是依赖其它物理因素,这在科学上就不是很完美。

人类永远不可能解开所有的疑惑。

读《松本行宏的程序世界》

这本书在一个月前就已经读完了,对于一个科班生来说,并没有太多新的内容。但是里面有一些很有意思的点,这里顺带写下来,以对之前付出的时间精力有个交待。

这是一本偏重编程思想的书,作者是 ruby 之父松本行宏。

作者在书里也说到,这并不是讲 ruby 使用的手册,而更多是使用 ruby 或者其它语言来说明某些思想。

不过这里还是想说一下自己对 ruby 这门语言的看法。在设计 ruby 语言时,松本行宏从 lisp 里借用了很多思想;而它的成功很大程度借助了 ruby on rails。这也说明了 ruby 有某些性质,更容易实现一个更为灵活易用的 web 框架。

书中大部分知识点都是一些最为基础、必知必会的内容,记笔记意义不会很大,所以这里只记录一下自己感觉有些意思的地方。

我为什么开发 ruby

这实际上是第1章的标题。作者讲自己开发 ruby,最开始是出于兴趣爱好,希望自己能够轻松编程,提高开发效率,不想开源后变得流行并变成了自己的一个职业。

计算机语言不少,作者学习其它语言,并根据自己的理解设计及发展了 ruby。从某种程度上说,一门语言的设计表现了作者对某些特性的取舍,实际上也是他对编程语言世界的理解。

作者对自己设计的 ruby 语言提出了 3 个设计原则:

  1. 简洁性
  2. 扩展性
  3. 稳定性

对于简洁性,作者形容为”能直接运行的伪码式编程语言”。应该算是很直观的描述了,单从结构形式上来说,ruby 编写的代码是比较易读的。

ruby 意为红宝石,是七月的诞生石。而 perl 音同 pearl,是六月的诞生石。可见 ruby 也从 perl 里选取了一些特性。

有一个段子说 perl 和 ruby 的作者是语言学家,而 python 的作者是数学家,所以前两者注重一个问题多种解法,而后者注重只提供最优解法。

实际上松本行宏也给出了答案,他希望使用这门语言是一件很有趣的事。当然,有一些语言也标榜自己的优势是乏味,毕竟做工程嘛。

对象并非对具体物体的反映

很多地方说面向对象是对自然界系统的一个模拟,或者说对现实世界物体的模拟。作者说这种说法是错的,我赞同这种观点。

为什么人们会有这种看法?

我觉得跟人们过度的类比有关。比如讲到类,大家习惯举一些现实中的例子以简化理解。比如,老师属于人类。

那么面向对象是什么呢?

最开始,软件的基本控制结构,只有一个跳转,也就是汇编里的 jmp。实际上现在常用的三种结构也都是使用跳转来实现的。

随着软件复杂度的上升,这种控制结构可读性很差,对于后期维护也是一个灾难,所以出现了结构化编程。通过将控制结构限定在顺序、分支、循环,来提高可维护性。

但是对于数据的维护,仍然是裸露在保护之外的,语言层面还没有一个较好的工具手段。这时,面向对象概念就出现了。

可以说,结构化编程是对控制流程的结构化,而面向对象是对数据的结构化。

说到这里,想起另一本书里的观念,计算机的一些概念本身就很美,比如变量地址,我们可以试着去理解它,没必要人为多加一层注解(房间号),试图绕过这种理解。

当然,从对控制结构的限制,我们也不难看出,通过越来越多的限制,我们确实获得了更多的自由。

总结

上面讲了两个我认为很有意思的地方。其它章节大多是技术上的探讨,这里就不缀述了。

从内容用词看,作者是一个很谦虚且喜欢思考的人。不过书里有一些地方存在重复,这本书是不是作者对旧有创作的二次整理就不肯定的。

对于科班生来说,大部分内容都是一些基础内容,之前应该都有涉及,不会有太大感受;如果没有学习过这些内容,或想温顾一下,还是可以一读的。

hexo博客搬迁

写博客的经历

最开始是接触 csdn,偶而记录一些现在看来无关痛痒的技术问题。

后来买了一台腾讯云的服务器,搭建了 wordpress,开始记录博客。

奈何服务器维护比较占用精力,而且于我博客互动较少,主要还是利已的。于是开始寻找类似 github page 这样的静态托管服务。

先是从朋友那里克隆了一份基于 jekyll 的代码,经过了一番折腾,好歹也用起来了,开始记录杂七杂八的东西。其间,也对 jekyll 做了一些扩展性的东西。

但是克隆到的主题看上去还是有些混乱,之前看过 jekyll 官方文档,说实话并不是很直白(比起 hexo)。遂决定迁移到 hexo。

hexo 迁移过程

迁移的整个过程是比较顺利的,hexo 的安装过程极度简单,几行命令就搞定了。

不过碍于网络问题,初始化时主题下载失败,打开空白,重新下载后 OK。整个过程下载比较慢,网络问题有时真的烦。

由于 hexo 文件格式与 jekyll 略有不同,简单写了一个 50 行的 python 脚本做了格式转换,比较顺利。

hexo 体验

hexo 也有主题跟插件机制,出于简单易用的考虑,后面有需要再做迭代。

这里使用了默认的 landscape 主题,整体是比较简洁大气的。

首页默认显示最近的十篇文章,也可以通过 Archive 查看所有按年编排的文章,或者通过 tag 过滤出某一类相关文档。

2020,博客可以继续写起来了。

事后记

记录两个事后发现的问题:

  1. 按照官方提示,接入 travis-ci 后,从 github 不能将分支正常切换到 gh-pages。github 官方在 16年已经 给出说明,2020年更新的 hexo 文档竟然没有修正,英文文档在页底有评论给出了解法。有需要可以参考这个仓库的 ci 文件。
  2. 小金反馈说,在迁移文件时,由于月份和日期在官方格式是双位的,单位会出现问题。这应该是 jekyll 兼容性过高引起的。

lisp论文《recursive》

论文的全称是《Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I》,是人工智能之父John McCarthy在1960年4月发表的。

之前读过一次,感觉很震撼。这次阅读下来,零零散散地加在一起,包括最后写的一个求值表达式的调试过程,总耗时大概有7个小时。

虽然时间已经过去60年,作者John McCarthy也已经离开我们7年,数学总归是数学,所以论文的语法,跟现在也不会有太大的差别。

重要的还是思想,借助文字来了解先驱所处的时代和想法,这也是我读这篇论文的初衷。

读到这样一篇踏实的论文还是很幸运的,虽然在最开始感到吃力。

这让我想起了《how to read a book》里讲到阅读的目的,比如看一则新闻,大多时候我们只是在获取更多的信息,对我们的理解能力并没有太大的提升。但是当阅读一篇在我们理解能力之外的文章时,虽然会有些吃力,但是它的回报是远远超过我们付出的。

系统的介绍论文的内容不是这篇文章的目的,且作者写的比我要详细准确一百万倍有余,我这里记录一下自己比较有感触的地方(意思就是跟这篇论文的内容关系不大 :-p)。

lisp并非无所不能

刚上来就要泼一下冷水了。

lisp很强大,你可以看我最后从论文里摘抄出来的一个 eval 求值函数的实现,一个可以用自己重写自身的语言,已经不能用强大来形容了。

lisp最重要的是思想,当然它也支撑了一些很重要的软件,比如emacs、autocad,但它终归不能作为商用软件的语言。

原因有很多,说一点,它的生态就比一些主流语言差很多,假如出现了一款新型数据库,社区要做语言的第三方库支持,肯定不会优先想到一门非主流语言。

所以即使lisp很好很强大,它的使用场景也很局限。

阅读这篇论文最大的障碍

我们记住一个东西或者学会一个东西很容易,但是要忘记就很难了。知识就是这样一个东西。

这是我最开始阅读时遇到最大的一个障碍,60年,还需要再过12年c语言才会被发明出来,至于java还要再等35年。

举个例子,如果我们熟悉了现在主流的语言设计,一开始可能会对 S-expressionM-expression 感到有些费解。

所以,我做的第一个尝试就是忘记之前所学的(表面)知识。

想起了张三丰跟张无忌的对话,这也算是我对这段对话的一个认识吧。

lisp的基础是数学

最开始有点难,但是当我理解了lisp设计的基础,一些东西就很容易理解了。

lisp的语言基础,是从数学的基本形式归纳出来的。如何将数学的逻辑和表达式抽象成一个统一的表达形式,是lisp要解决的一个问题。

有的人说lisp很美,大多时候指的并不是它的表现形式(当然,可能也有人觉得lisp的括弧很美),而是指它的底层结构。这也是它可以很方便地重写自身的原因。

这种古老的语言(而不是现代的语言),让我更加确信计算机科学是数学的一个分支。

现代语言的设计,基于计算机的比例变得越来越高。

一个原因是,数学是一门语言的核心,核心的东西一般都较小,而且现在语言支持的特性越来越庞大,不可避免会有越来越多的非核心的面向计算机的代码。比如,多核心,多线程,有越来越多的计算机术语被发明出来。

关于波兰表达式

波兰表达式(polish notation)和代数表达式(algebraic notation)是两个很典型的数学表达式(还有逆波兰表达式,如emacs的calc)。

最直观的方式是代数表达式,面向人类。

波兰表达式的优点在于它的结构更加统一,更易于流程化的处理。还有很重要的一点,它可以做到数据和运算分离,这可能是emacs的calc工具使用逆波兰表达式的原因。

比如,我们经常会计算一个月内的开销,有时就是一些小花费的总和,这时如果使用波兰表达式就很清楚明了,(+ 30.0 20.0 30.0 40.0)。

声名式和命令式(declarative and imperative)

这两个概念在60年就已经存在了,不过现在有一些人还是不太清楚。

一句话,初级领导用命令式,高级领导用声明式。

内存管理

是的,内存管理在60年的lisp里就已经实现了。使用lisp的程序员们不需要关心内存释放,甚至都不需要知道存在内存这样的东西(当然,没人不知道)。

由于lisp的基础很简单,所以lisp内存的实现也很简单。这是一个良性循环。

函数名的设计

这在现在看来已经是理所当然的了。

但是在最开始的设计中,如何实现数学中的函数是一个值得被推敲的事情。

lisp使用了 Church 中定义的 lambda 的原型,把函数实现中的某些参数(形参)和实参绑定,以此为上下文对实现进行求值。

但是 lambda 不能解决递归的问题,比如辗转相除求最大公约数或者利用牛顿公式求解微分方程。

函数名的设计可以解决这个问题,即将名称与实现绑定,实现中就可以使用名称来递归自身了。

lisp中的数据结构

所谓一生二,二生万物。

lisp中的基本数据结构只是一个包含两个元素的元组,但是元组可以互相组合,它的表达能力是无穷的(这里的表达能力指的不是可读性,而是表达的内容)。

而且在形式上有一个莫大的好处,就是它非常适合实现递归的思想。

这里插播一段代码,比如定义一个函数,将 APPLE 转换为 BOY

(defun replace-apple-to-boy (x)
  (cond ((atom x) (or (and (eq x 'apple) 'boy) x))
    (t (cons (replace-apple-to-boy (car x))
         (replace-apple-to-boy (cdr x))))))

(replace-apple-to-boy '((apple foo) apple)) ;; => ((boy foo) boy)

lisp的学习成本

是的,学习lisp几乎没有学习成本。主要成本在lisp的思想上,即递归,这几乎是学习所有语言必知必会的东西。

lisp的基础构建在几个原子操作上,学习的时间应该可以用分钟来计了。

所以emacs使用elisp来作为扩展语言,甚至很多非计算机专业的人来了兴致也可以敲上几行代码。

觉得lisp很难的人,很多都是有其它现代语言基础的人。

过程中遇到的一些小问题

html版本有个好处是组织结构更清晰,一章一个链接,不过公式排版差强人意。

pdf版好一些,但是在eval的定义一块格式还是有问题,特别是嵌套层次深了以后,很难一眼看出自己在哪里。最好是自己重新排版一下。

网络上还有一些排版更友好一些的版本,不过差别不会太大。

eval

这是lisp很强大的一个经典论证,即在语言之上实现该语言的求值器。

NOTE: 该代码使用了elisp,在其它lisp方言应该也可以正常运行。其中只使用了有限的几个函数(car cdr cons cond eq atom)。

(defun assoc2 (x y)
  (cond ((null y) nil)
    ((eq x (caar y)) (cdar y))
    (t (assoc2 x (cdr y)))))

(defun append2 (x y)
  (cond ((null x) y)
    (t (cons (car x) (append2 (cdr x) y)))))

(defun evcon (c a)
  (cond ((eval2 (caar c) a) (eval2 (cadar c) a))
    (t (evcon (cdr c) a))))

(defun evlis (m a)
  (cond ((null m) nil)
    (t (cons (eval2 (car m) a) (evlis (cdr m) a)))))

(defun pair (x y)
  (cond ((and (null x) (null y)) nil)
    ((and (not (atom x)) (not (atom y))) (cons (cons (car x) (car y)) (pair (cdr x) (cdr y))))))

(defun eval2 (e a)
  (cond ((atom e) (assoc2 e a))
    ((atom (car e))
     (cond ((eq (car e) 'QUOTE) (cadr e))
           ((eq (car e) 'ATOM) (atom (eval2 (cadr e) a)))
           ((eq (car e) 'EQ) (eq (eval2 (cadr e) a) (eval2 (caddr e) a)))
           ((eq (car e) 'COND) (evcon (cdr e) a))
           ((eq (car e) 'CAR) (car (eval2 (cadr e) a)))
           ((eq (car e) 'CDR) (cdr (eval2 (cadr e) a)))
           ((eq (car e) 'CONS) (cons (eval2 (cadr e) a) (eval2 (caddr e) a)))
           (t (eval2 (cons (assoc2 (car e) a) (evlis (cdr e) a)) a))))
    ((eq (caar e) 'LABEL) (eval2 (cons (caddar e) (cdr e)) (cons (cons (cadar e) (car e)) a)))
    ((eq (caar e) 'LAMBDA) (eval2 (caddar e) (append (pair (cadar e) (evlis (cdr e) a)) a)))))

下面这个例子使用了前面定义的 eval2 ,来拼接两个参数里的首元素。

(eval2 '((LAMBDA (x y) (CONS (CAR x) (CAR y))) (CONS (QUOTE A) (QUOTE B)) (CONS (QUOTE C) (QUOTE D))) nil)  ;; => (A . C)

这个函数有一个缺陷,在使用 LABEL 进行递归求值时,会再次对求值后的结果进行二次求值,导致错误。

论文中提到有一个修正版本,发布在91年的《Artificial and Mathematical Theory of Computation》。在sciencedirect.com上找到一个收费版的地址,没有继续下去了。地址在下方。

图书管可能有这类书?

引用

http://www-formal.stanford.edu/jmc/recursive.html
https://www.sciencedirect.com/book/9780124500105/artificial-and-mathematical-theory-of-computation

莉莉安

《莉莉安》

是一个黄昏,天色渐暗,我爬上房顶,等着日落。

脚下是我自己搭建的木头房子,房顶扁平,有一根藤梯连接到这里。

四下没人,只有高大的热带树木,另一侧是一片大海。干净的空气,安静有暖风。

太阳在大海的一侧渐渐落下去,周围越发昏暗,远处一片绯红。

落莫凄美的夜,远处泛着白光。

《理想三旬》

慵懒的下午,窗外下着连绵的雨。是南方的三月。

陷在陈旧的沙发里,破碎的窗口有风吹进来。淡淡的霉的气息,让人心情舒畅。

皮革箱丢在一边,被旧衣物围成了一个圈,像是一个倍受尊敬的老伯。

窗上还牵扯的纸片飘摇不定。

GTD实践分享


关于写作

现在几乎一周可以出一篇文章,闲暇里我也想过这个问题,也获得了一些结论。

关于写作时间 :对我来说,写作已经不需要消耗多少时间了,因为我在电脑前坐下写作之前,文章的内容和大体框架实际上已经完成了,这只是一个再现的过程,或者把之前整理的思路重现的过程。是的,写作其实是走在路上完成的。

关于写作目的 :现在写作的目的已经完全不是我预想的目的了。最开始的想法是可以帮助他人,但是写作这件事已经摆脱了我的设想,朝着自定的方向发展了。他想做什么呢?

以前跟小郭出去逛街,不像真实意义上的逛街,不重在吃和买,倒像是一次长谈。我们一边走一边讨论生活,交换价值观,很多思想也都互通有无。我喜欢这种交流。

而现在的写作,更像是跟自己进行的一次长谈。平时很少有机会可以静下来跟自己聊聊,现在有了一段跟自己交换思想的时间。当然也是跟看到这篇文章的人交流,当然,交流的目的还是互通有无,不问对错,只需要了解。从这一点上来说,写作是自私的。

其次呢,写作更是一种学习的途径。我看很多人的技术博客或者文章,都是对过去的一些事情的总结。我现在写作并不是对过去的总结,或者在必要的时候总结过去只是为了更好地梳理以帮忙到现在或未来。每当我想学习一个新东西的时候,就会在日程上安排一个写作计划,制定一个deadline,积累素材的过程实质上就是一个学习的过程。

关于第二点,划下重点, 写作不是总结

在写作的过程中,我还会有一些额外的收获,比如在写作这篇文章时,在希望有一个手机全局软件可以作为GTD收件箱的时候,我想到了闪念胶囊,但是由于苹果的封闭性,市场里只能找到一些拙劣的替代品。虽然锤子科技17年有说ios版在开发中,可以已经流产了。

一句话,如果想学习某个东西,就制定一个写作计划(思想)或者做一个成品的计划(技能)吧!

时间管理简史

混沌期

学生时代并不需要管理时间,时间都是被学习管理好的,明明白白。

到了大学的时候,也不太需要管理时间,想做什么做什么,想做的不多,时间倒挺多。

这段时间我称它为混沌期,或者叫被动管理期。一直延续到工作以后。

初见workflowy

工作以后,时间安排的重要性就慢慢体现出来了。

随着时间的推移,我们cover的事情越来越多,而精力又越来越少,不得不将自己的时间管理起来。

时间管理,其实是精力管理。

工作以后,我们或早或晚地会意识到计划的重要性。它可以指导我们时间或精力的分配,不至于因为事情太多感到茫然无助。而且在汇报工作时,也有一个好的参考。

最开始我是记在文本里的,一天一记,定时总结(大部分时候都感觉总结没必要,就直接跳过了。走到某天意识到遗失了一批最有价值的宝藏 :p)。

PS: 刚才突然意识到很多人为什么用:-p而不用:p,因为:)在编辑器里的括弧会匹配到左括弧我的天。

后来,认识了workflwy(好像是小金推荐的),瞬间被它的简洁和优雅打动。我甚至极度迷恋到以为可以用它来管理我的整个后半生。

最初使用的过程中,除了加载会有点慢,没有发现其它的问题。我至今都不明白,zf做的这些“努力”到底是为了什么。

不过在后来的一次更新中,登录后数据怎么都加载不出来。跟开发者反馈后,经过复杂的五步操作,解决了这个问题。后来这类问题又反复了几次,最终决定卸载。

在写这篇文章前又试了一下,还没有恢复,也可能是手机缓存的问题。不过我不想再把自己的精力浪费在别人反复出现的问题上了。这时候,我意识到纯文本管理的便捷性,这是我使用org-mode的一个重要原因。

我换用了中国人开发的workflowy copy版app幕布,数据加载没有问题。唯二的缺点是使用更复杂和夹杂着使用体验上的bug(键盘RETURN键失效)。第一点是团队的设计原则问题,没法改变。第二点已经反馈给官方。

不过,GTD才是解决时间管理的真正方法。

由于我写作大多是在闲暇时间完成的,所以习惯用手机构建,workflowy类软件多终端同步,可以解决这个问题。注意,workflowy适合整理思路,不应该被用来做时间管理。

apple notes

这是我在换到苹果全家桶后非常喜欢的一款软件,它简单,开箱即用,支持多终端。

我曾经有很长一段时间用它来记录信息,支持图片,搜索也方便。我还用它来写文章,写完后直接拷贝到blog就可以发布了(后来意识到用它写文章是不方便的,手机适合助攻,电脑才是王道)。

甚至我尝试过用它做todo,不过在做todo的过程中,发现了一堆bug,皆因它自动把todo条目转换成图形导致。这让我再一次意识到纯文本的重要性。其实转换不是问题,但是操作的一定要是文本,否则出了问题就是单向不可恢复的。

拥抱GTD,拥抱org-mode

如果你没用过GTD,我要说明一点的是,它不止是todolist。

这一点值得被反复强调,它不止是todolist。

一个很重要的关键词是收件箱,GTD是一个系统,它会把你接收到的思想或者事件,都放到收件箱里。收件箱会再次被转交到GTD系统,来缓解我们在记忆上的压力和精力分散的问题。

为什么要单独提收件箱,因为org-mode里的GTD已经很完整了,经过少量的配置就可以高度可用。剩下的问题只有,如何跟外部打通。最为关键的就是,用什么来做收件箱?

如果使用org-mode,电脑端已经毫无疑问是remember命令,但是手机端呢?当我们离开办公场所,离开家,只有一个手持设备时,如何快速记录临时的想法呢?

我现在仍然坚定的认为,闪念胶囊,或者说闪念胶囊之类的设计,是最理想的收件箱。

我尝试过使用notes来做收件箱,但是效果并不理想。打开notes需要很多的思考,它不是“不假思索”的。而且手机上的回车和电脑上的回车有天壤之别,电脑上的回车跟其它键是一致的,但手机上的回车跟字母键(在人的直观感觉上)是分离的,而且回错了要删除,删除键也是分离的。

退而求其次的方法是使用一个todo工具或者workflowy类工具。

任务类型的思考

就我个人的思考,任务分很多种,每种任务的处理方式是不一样的。

对任务类型的分类,是这篇文章中思考密集度最为集中的地方,也是这篇文章指导我要思考的点。

任务的分类并非严格按照重要紧急四象限排列,在GTD的实践中,我发现它们之间有些许不同。所以我决定先忘记四象限,按照GTD的思路整理一下。

临时性紧急任务

此类任务可能会打断我们当前的状态,需要立即去执行。

比如刚才我在写作的过程中,突然外卖到了,门铃响了。这就很紧急,如果我不给他开门,中午就没饭吃了。

临时性非紧急任务

此类任务也会打断我们的状态,但是由于不紧急,可以将它加到 收件箱 ,然后快带恢复到之前的状态。

它会耗费我们一定的精力,但是不多,包括两方面:一是任务细节的沟通,二是加到收件箱的操作。所以为什么我很重视收件箱的设计,因为它是保证我们可以快速恢复到正常状态(之前状态)的非常非常重要的一环。所以收件箱最好是全局的,是可以一键唤起的。

这类任务当我们整理收件箱的时候,会加入待办,或者更进一步,对待办排期。

一个很好的例子就是,你的朋友打电话约你周末去图书馆看书。

注意,上面这两类任务还不能称其为任务,或者还没有进程GTD系统。因为它们一个不必要进,一个还在收件箱里等待进。

后面的这几类任务,就都是GTD系统里的任务了。

scheduled任务

临时性非紧急任务可以转为排期任务,它很重要,但是不会立即去做。它不仅是重要不紧急,而且还有一个属性,就是利用一段确切的时间(比如3个番茄钟)是可以完成的。

deadline任务

这类任务比较有目标、有雄心、有毅力,它表示你要在一段时间内努力达成某种效果。

比如,你计划在半年内打到《王者荣耀》王者段位。这类任务需要很好的自控能力,因为达成任务需要消耗的时间不确定,如果你没有自信,可以把任务细分为子任务,这样就拆解成了排期任务,实现的难度就大大降低了。但是没准,你就是想挑战一下自己呢?

repeat任务

这类任务挺悠闲,没有任务,或者说完成它们本身就是任务。这类任务更侧重提醒功能,去做而不是做完。

比如,我做了一个repeat任务,为了保持自己的形象,每隔两天刮一次胡子。

maybe任务

这类任务还没有排期,不重要也不紧急,甚至不一定要做。它只是我们的一些奇形怪状的想法,可能在某天突然来了灵感或者突然变得重要,然后就被加到我们的排期里。也可能这辈子都不会被我们注意到了,谁知道呢?

但是一定要记下来,好让我们知道,我们已经考虑过了,而不是再考虑一次。

哪些任务需要记录耗时

我喜欢org-mode里的org-clock-in功能,它能让我知道做一件事耗费的时间。

当然,工具就是工具,不是所有的事情都需要知道耗时的。

比如,我每隔四天做一次俯卧撑,但是并不需要知道它的时间。根据我最开始的计算,每次都是3分钟左右。

阅读是一个与之相对的例子,因为我每次阅读是以章节为单位的,而不是时间,所以我需要记录每次阅读的时间,以知道读一本书需要多在成本。我不喜欢那种每天花 半小时 读书的计划,它太死板,书有逻辑性。

再比如写作,不同的文章内容,耗费的精力和时间是不一样的。当我在写下这篇文章时,编辑器的下方会提醒我在这篇文章上花费的时间。看,已经1小时40分钟了,不过这篇文章也已经接近尾声了。

最后一句话,适合自己的才是最好的。

emacs keymaps

Table of Contents

  1. emacs中的keymaps
    1. keymap结构体
    2. 行为覆盖
    3. 两个特化的问题
      1. prefix keys
      2. mode继承
    4. 结束

emacs中的keymaps

emacs具有很高的可配置性,从keybinding,customize,到UI,再到各种第三方package。

keybinding是学习emacs的过程中必经的一环,很难相信有人会不加修改地使用原生的emacs按键行为。

当然,keybinding由来以久。甚至在很多现代的软件中,给它取了一个更激动人心的名字,叫做快捷键,像是一项了不起的发明。其实在emacs里,是最最基础的一项功能。

在这篇文章里,我将尝试从底层的概念开始,讲解emacs里的键绑定机制,以让读者可以自由地定制emacs里的按键,并知其所以然。

keymap结构体

emacs的keybinding有一个基石,或者说设计原则,那就是每一个按键都绑定到了一个函数。从键盘输入,到鼠标,再到菜单,无一不在这个指导原则之下。

试想一下,如果我们来实现这样一个功能,必然是需要一个结构体来关联按键和功能函数的。这个结构体,可以帮助我们快速找到一个按键对应的功能函数。

emacs里使用的列表叫做keymap(大部分编辑器都使用这种称谓)。我们可以使用(keymapp)来检查一个结构是否是标准keymap,实际上它是通过检查列表的第一个元素是否是’keymap来判定的。

(keymapp '(keymap xxxx))  ;; t

我们可以找一个keymap来看一下它的结构:

M-x describe-variable org-mode-map

(keymap
 (25 . org-yank)
 (11 . org-kill-line)
 (5 . org-end-of-line)
 (1 . org-beginning-of-line)
 ;; ...
)

如上面的代码片段所示,首元素以后的其它元素(cons),由按键和绑定的函数组成。如果我们对25求值,可以看到它对应的按键就是?\C-y。

当然,(key . func-map)只是一种最简形式。比如,我们还需要支持一些按键序列,例如C-x C-f,这个结构就需要扩展一下了。后面再说。

emacs为keymap操作提供了丰富的函数,可以使用下面的例子创建一个keymap,并添加C-;映射:

(setq map (make-sparse-keymap))      ;; (keymap)
(define-key map "\C-x" 'c-x-func)
map  ;; (keymap (24 . c-x-func))  24 => ?C-x

行为覆盖

在上面的例子中,我们已经创建了一个keymap并绑定了C-x的行为。但是它现在只是一个值,如何将其应用到我们的emacs中呢?

不急,要回答这个问题,我们要首先了解,当一个按键按下后,emacs是如何找到它绑定的函数的。

我们知道,在vim中绑定一个按键,可以使用:map命令,如果首参给个,它将会作用到当前buffer而不修改全局keymap。(插播一句,由于vimscript只是一个专一化的脚本语言,vim的map行为是在vim内部执行的。我们还不能在vimscript层面可以直观地获取它对应的map)

emacs也存在这种类似的参数。当然,keymap的查找被定义为更严格的一个逻辑顺序。

说了这么多,emacs查找按键的顺序有三个:

  1. 查找一个神秘但对我们并不重要的map。
  2. 查找’minor-mode-map-alist。
  3. 查找(current-local-map)。一般会由major-mode设定。
  4. 查找(current-global-map)。一般会等于global-map变量,是emacs的默认全局配置。

好吧,如果你真的对1感兴趣,其实它是一个粒度更细的map,它会绑定到一个buffer里指定的某些文本。如果这个解释不能满足你,可以看一下text-property相关的手册。

2、3不言而喻了,需要说明的一点是,如果我们知道某个mode的名字,可以很方便地找到它对应的map变量,如org-mode对应的keymap为org-mode-map。

现在我们知道了keymap可以定义的多个层级,text-property -> minor-mode -> major-mode -> global-map。

上面我们讲到一个(define-key)函数,可用来修改keymap变量里的某个绑定。现在你应该已经知道网上某个家伙emacs配置里的代码片段是什么含义了:

(define-key 'global-map "C-x C-e" 'func)

两个特化的问题

主要的内容已经讲完了,下面再赠送两个额外的内容。

prefix keys

我想试着绕开这个内容,但是发现做不到。如果真正碰到了,会感觉很迷惑,不如两笔带一下。

我们上面提到,keymap的元素不一定是(key . map-func)的形式,比如C-x C-f,它实际上是两个序列的组合。在emacs中使用这样的形式来定义:(key . keymap2),即它的map-func是另一个keymap。

举个例子,假设我们想在全局的keymap下,定义一个前缀输入为C-;的序列,让它在我们输入C-; 2时,在2的后面添加一个;(只是试验,毫无价值)。

(defun append-semicolon ()
  (interactive)
  (self-insert-command 1)
  (insert ";"))

(setq ctl-semicolon-map (make-sparse-keymap))
(define-key ctl-semicolon-map "2" 'append-semicolon)

(define-key global-map (kbd "C-;") ctl-semicolon-map)

这时,如果我们输入一个C-; 2时,编辑器里就会输入一个2;。

需要注意,此时,如果我们修改global-map的C-; 3,修改的实际上是ctl-semicolon-map。

(define-key global-map (kbd "C-; 3") 'append-semicolon)
ctl-semicolon-map  ;; => (keymap (51 . append-semicolon) (50 . append-semicolon))

emacs里的C-x等序列就是这样定义的。

mode继承

emacs里的mode是有继承关系的,例如所有的编程语言mode都 应该 继承自 prog-mode ,所以针对编程语言的通用keymap,可以直接修改 prog-mode-map

具体的细节,可以查看mode设计的手册。

结束

以上的内容就可以把整个keymap串起来了,如果有什么地方不了解,所有需要的知识都在 C-h i 里。尽情查阅吧。

高可配置的emacs

Table of Contents

  1. customizable emacs
    1. 源码设计
    2. 软件设计
    3. 扩展语言
    4. Emacs并非无所不能

customizable emacs

Emacs是世界上可配置性最强的编辑器。

我最开始试图在上面这句话里,加上“几乎”或者“之一”来让这句话变得更准确。后来发现不管加上哪个词,这句话都会变成一句错误。

是的,世界上没有任何编辑器,在可配置性上能超越它。

争论这句话的对错已经没有意义,我现在想知道,为什么它可以在可配置上能够达到极致。或者,为什么其他编辑器达不到这种可配置程度?

这篇文章将尝试从一个矿工的视角,把最宝贵的这部分内容挖掘出来。

源码设计

linux kernel内核用到了很小一部分的汇编源码,据说新版本已经完全用c来实现了。

如果不是迫不得已,我相信linux kernel还是倾向避免接触到汇编的。

Emacs也遇到了这样的问题,由于效率上的考虑,它不得不将一部分底层代码使用c语言来构建。从本质上来说,Emacs是一个用c语言实现的lisp解释器。

剩下80%的部分,Emacs是使用在c上构建的lisp语言来编写的。

elisp更接近脚本语言,它有点类似python(sorry,其实是python有点类似lisp),也可以编译为字节码。它的一切都是可读的,任务一个lisp语言的函数,都可以快速找到它对应的lisp源码。

这就为用户提供了接近真相的机会,如果你愿意,甚至可以根据自己的需要直接修改核心的Emacs(好吧,你最好不要这么做,这意味着你已经脱离了Emacs和其他Emacer的支持)。

软件设计

软件包括两部分,可执行的二进制或脚本文件和它所包含的文档。

Emacs的核心软件都是结构化的,这使得我们可以很容易地找到相关功能所在的源码。比如,如果你想研究Emacs中的window操作实现,可以找到window操作对应的lisp文件window.el。

文档部分也是经过了精心设计的,从C-h对应的完整的文档分类可见一斑。这使得Emacs的用户很容易地可以查找到一整块完整的功能,读过Emacs手册的人应该都深有体会。

这里需要说明一点的是,Emacs的文档系统其实还包含了动态文档系统,比如查看当前mode或key bindings。所有的文档组成了一张严密的网络,使得我们在Emacs中可以快速地到达想要去的各个地方。

扩展语言

众所周知,Emacs使用了自己用c语言实现的一种lisp方言:elisp。

我认为,elisp被大家过分夸大了,甚至给人一种“Lisp不同寻常的语法决定了其开发者都是资深开发者”的印象。

甚至,我认为,恰恰是elisp的一些缺点,才导致Emacs的扩展性达到了无与伦比的高度。

lisp的括号可能是最被人诟病的设计,有一个lisp笑话被lisp黑客广为流传:“据说一个黑客偷到了美国用于导弹控制的LISP代码的最后一页,却发现这最后一页全是右括号”。

你可能写过python或者c语言这种面向对象或者面向过程的语言,但是遗憾的是,在lisp的世界里,这两种编程范式都很难被广泛实施,有很大原因源自lisp的括号设计。括号的大量出现,决定了你很难一口气写出100行的代码(希望你有勇气接受这个挑战)。所以它强制你精简每一个函数的实现,大量的微小函数组成了一张紧密的网,来提供强大灵活的整体功能。

这其实就是自底向上的编程思想了,没错,lisp强制我们使用自底向上的思想来思考问题。

说到自底向上编码,它的好处在哪里呢?它里面其实包含了分层的思想,即下层的功能可以很容易地被上层代码复用,而且当粒度足够小时,几乎所有想要的功能都一触即达。我们想要的只是一个编辑普通文本的编辑器,最后一不小心就变成了一个操作系统。

当我在借用第三方package实现某个功能时,一下子就找到了我所需要使用了所有函数,我需要做的只是使用自己的风格封闭它。

据说,当你打算设计一个极度复杂的系统时,lisp会让开发成本和维护成本随复杂度呈指数下降。Emacs就是一个例子。

“凡有的,还要给他,让他丰足有余。”

Emacs并非无所不能

最后的这个标题出人意外,前面夸赞了那么多,最后要承认它只不过是个使用习惯令人蹩脚或者过时的编辑器吗?

不是的!

我想知道网络上,Emacer们都在干些什么(问题来自知乎):

  1. 你认为最好看的 Emacs 配色方案(color scheme)是哪款?
  2. emacs还能做什么?emacs比vim好在哪里?
  3. Vim 和 Emacs 分别适合哪些人群?
  4. 谁知道emacs lispy-mode的用法?
  5. 对于使用emacs包管理器ELPA,你有哪些推荐的包?

我之前是一个vim用户,想学习Emacs多次未果,主要是觉得Emacs的按键太反人类。但是这是一种先入为主的思想,现在看来,应该让Emacs来适应我们,而不是本末倒置,要知道,Emacs就强大在它的可配置性上。

不要在自己还没有尝试了解一个东西之前,试图通过直接询问的方式一劳永逸。可以临时解决问题,但是不要在自己还不清楚的事情上浪费太多时间,最后原理没了解,只是在东拼西凑试图猜中答案。

不要做一个完美主义者。我之前也喜欢折腾,从字段,到配色,最后发现,这其实跟不动脑思考只知道蛮干是一样的。要精进不要盲进,少即是多,容忍不知道。

另外,不要试图寻找自己不需要的答案,也不要试图通过询问一次性获得答案。我们不需要知道别人使用哪些package,自己用的顺手才最重要。第一手资料在官方手册或者包列表服务器,我们要找的答案,绝不会简单地摆放在google里。

使用emacs管理生活

Table of Contents

  1. 使用emacs管理生活
    1. 关于这篇文章
    2. 一天应该从一杯牛奶开始吗?
    3. 一天从emacs开始
    4. 来个番茄?
    5. 不能煮咖啡的编辑器不是一个好操作系统
    6. 附件

使用emacs管理生活

关于这篇文章

很久没有写文章了,我是在一周前决定在今天写这篇文章的,用来分享我在emacs上的实践。在这周里,我有时闲下来会想到这篇文章的一些碎片,零零散散,错落有致。

我是在一两周前转到emacs的,在之前我一直使用vim作为我的编辑器。当然,只是编辑器,因为我的大部分时间都是用来编辑文本(代码、配置)。

我转到emacs,最开始是借助了spacemacs,它让我看到了emacs与vim共存的可能。但是因为spacemacs过于范化,不能满足我自己的使用习惯,我决定开始打磨自己的emacs配置。

emacs最令人惊叹的是org-mode,或者更准确地说,围绕org-mode建立起的生态。如果你没有尝试过,建议你在合适的时间研究一下这个模式,相信我,它会彻底改变你的生活方式。

由于这篇文章主要是分享自己的一些感受,所以不会涉及太多代码(你知道的,在emacs的世界里,存在大量elisp代码片段)。

一天应该从一杯牛奶开始吗?

每个人都有自己的节奏,我以前的节奏就是,混乱地开始和混乱的结束一天的生活和工作。如果我们有能力使这种模式免于混乱,便可以得到一个更为高级的名字,叫做自底向上的生活方式,过好当下,然后等着上天赐予我们最好的礼物。拖延症患者的生活方式并不是自底向上,而仍处于混乱状态(譬如我),所以还算不上自底向上。

对应的方式就是自顶向下了,我们可能会制定一个N年的计划,比如一个长达三年的学习计划,然后逐步分解去执行。不过实际情况往往会跟目标有出入,可以选择分片段逐步细化的方式。

在经历过长达数年的混沌时期后,我决定让自己逐渐适应并且切换到了第二种方式。我需要一个理论支持,最好是已经成熟并且有很多人实践过的理论。于是我找到了GTD。

关于GTD(Get Things Done),有一些专门的书籍可以学习。不过在我看来,一个好的实践,一定是不需要太多繁杂步骤的,简单而且行之有效。所以我并没有从头到尾阅读书籍,而是了解了GTD的思想,以及它的大体步骤。

GTD最核心的思想,可以用一句话来形容:将头脑中的想法记录到载体上。这样做的好处是什么呢?

首先,大脑没有了负担。设想之前的工作或生活,突然,脑海中想起了一件事,去做或者一会去做,有可能一会又忘记了,大脑承担了很多思考外的低级功能。

其次,待办更清晰了。只要我们能拿到所有的待办,就可以对待办进行评级、排期。只要一件事情做好了排期,我们只要等到对的时间去做就可以了。

一句话,解放大脑,提高专注力。

官方的GTD步骤有五个,我在这里整理下我的实践步骤:

  1. 收集:将大脑中的想法、邮件收件箱或其它的工具的信息收集起来。对于记录脑中的想法而言,手机是很方便的一个工具,我喜欢使用mac自带的Notes来记录脑海中一闪而过的想法,还可以直接同步到电脑端打开,再次处理都很方便。

  2. 整理:收集到的信息,需要进行再次处理,明确意义后归类。比如一个待办的具体定义,要归放到哪个项目下。

  3. 排期:对待办设置优先级,并查看自己的排期,将待办加入到自己的排期中。我自己一般不会对某个待办设置优先级,而是直接对待办所属的项目制定优先级。比如,我在跟进一个非常重要的项目,那它下面的待办就理应具有更高的优先组。

  4. 执行:这个步骤就相对简单了,因为信息已经做了梳理和排期,到了合适的时间,按照计划执行就可以了。

当然,第4个步骤简单,但也困难。困难之处在于,有很多情况是我们预料不到的情况。比如,可能会有其它同事来打断,或者有更高优先级的突发事件,这就需要我们根据其它方法论来处理了。碍于篇幅,这种情况我们暂不展开。

讲了这么多,还没有回答上面的问题。一天应该从一杯牛奶开始吗?对于我来说,答案是否定的。我的一天是从GTD开始的。

一天从emacs开始

市面上有很多GTD工具,可惜我都没使用过。我的工作几乎全部使用电脑完成,所以需要一个更加无缝操作的GTD工具,它最好不用分散我太多的注意力。

emacs里有一个org-mode,可以用来管理TODO列表,例如修改TODO列表状态,对TODO排期,或者记录TODO用时。

org-agenda是一个org-mode的相关工具,可以自定义项目的TODO列表的展示逻辑,比如显示今天有哪些代办,或者显示有哪些项目处于stuck状态(某个项目下没有TODO列表)。

用一句很装逼的话来说,“我已经一点都记不起明天需要做什么了”。这应该是我的一个追求吧,现在还没有完全达到。

到公司后,我的第一件事就是打开emacs。我对它进行了一些配置,打开软件后它会自动为我显示这一天的待办记录,这一天将从查阅待办开始。

在开始某一个待办前,我会使用org-clock-in对待办进行计时,以此来跟踪我在一个待办上花费的时间,并且在完成一个待办后休息一小段时间。

世界从未如此高效而轻松。

来个番茄?

我还找到一个提高专注能力的工作,番茄钟。

发明者为了提高自己的效率,发明了一种时间分片的方式,每25分钟为一个番茄钟,每个番茄钟结束后休息5分钟,每4个番茄钟休息15分钟。

这是一种很好的时间管理方式,它能让我们感知到时间的流逝,更好的对某件事所花费的时间做出评估。

我曾经实践过这种方法,但是发现它过于死板不够灵活,于是根据自己的需要进行了部分改造。

仍然是25分钟一个番茄钟,但是我可以在必要的时候提前或推迟结束时间。比如,某个待办进行的很顺利,20分钟就完成了,那我会选择提前结束这个番茄钟;如果某个待办需要30分钟,我可能会选择延迟5分钟。当然,有一种情况是这个待办需要1个小时,我会在25分钟左右,在合适的时机结束番茄钟。

一句话,节奏感很重要。节奏感是一种感觉,即是感觉,就会因人而异,需要找到适合自己的方式或节奏。

emacs是有包来专门提供番茄钟功能的,我没有使用。在我看来,没有太大的意义。

不能煮咖啡的编辑器不是一个好操作系统

你可能已经听说了,emacs是一个操作系统,而非编辑器。

这并非对emacs的嘲讽,实际上我认为这是对emacs的高度评价。

我想先谈一下gnu/linux或者windows操作系统,如果细想一下,“操作系统”这个词并不是很准确,如果你用过gnu/linux,你可能会同意我这个观点:gnu/linux更像是一个资源管理系统,它的任务更多的是接管底层硬件资源并提供调度方式以最大化使用效率。gnu开发了一个套件,用来实现一些与用户交互的功能,但是这是不够的,我们很多时候还是会安装许多额外工具。

所以称emacs是一个操作系统其实蛮贴切的,错的不是emacs,而是操作系统的定义。

你可能也听说过一种现象,emacer们倾向于把自己想要的所有功能都记录到emacs里,他们在emacs里浏览网页,阅读rss,收发邮件,可能甚至有人想用它来控制自己的洗衣机。

我想试着来解释下这种现象,前面我们说过emacs是一个操作系统,甚至它比gnu/linux做得还好,因为它高度定制,且有一致的操作风格。你可以把它看做是一个根据使用者的使用习惯高度特化的操作环境。

举个例子,比如我现在在编辑当前这篇文章,但是感觉这个自然段的表达方式有点问题,但是现在我的关注点是先把这篇文章写完,此时我可以使用remember命令调出一个临时记录note的buffer,记录一下现在的想法,这时想法就被记录到一个叫做remember-notes里了。我可以继续编辑这篇文章,使我的思绪被打算的可能性降到最低。

所以答案就很简单了,因为这个“操作系统”里已经集成了很多可用的工具,如果把另一个工具集成进来,效率和便利性会成指数级增长。

但是这是一个致命的诱惑,你感觉这是一个充满花蜜的骨朵,很有可能它会把你粘住拖进它的花苞消化分解掉,变成它的食物。

如果你像我一样,没有太多的精力去做这些看上去很有乐趣的事情,那就一定要克制住自己了,一定要守住自己的边界,知道什么不可以做。

附件

img

当前正在做的事情被高亮显示(图中为黄色),且会在modeline显示。

如果某个昨天的待办没有完成,Scheduled会变成Sched. 1x,并被加到今天。比如,写作emacs使用实践和腹肌撕裂者两个待办昨天都没有完成(现在已经是第二天的00:22)了,所以被加到了今天。

有一种任务是每隔几天就做一次,比如刮胡子这个待办需要每三天做一次,使用 2019-03-03 Sun +3d 时间,系统就会每三天生成一个待办。

img

我喜欢给一些需要随意做的任务记录时间,这样就可以知道我做某件事的整个时间周期了。成就感悠然而生。

spacemacs从入门到放弃

我的vim史

我是一个vim编辑器的重度用户,使用vim已经十年有余。从日常记录文本、开发到远程服务器的运维,它一直是我必备的工具。我有自己的一套配置,裸用vim也不在话下。

我在很久之前接触lisp,并为之倾倒。从来没有哪一门语言的外在表现可以如此完美统一,我相信一门编程语言的设计是完全可以塑造一个人的思维。谈及lisp,几乎毫无疑问谈到emacs编辑器。

这里顺带谈一个我对emacs设计理念的理解,emacs在我看来是lisp精神的一个很完美的体现(并不是说emacs完美)。emacs使用了20%的c语言代码,实现了一个lisp解释器,然后使用其余80%的代码,在lisp上构造了整个编辑器。所有的操作都对应着lisp函数,使得所有操作都是清晰和可查的。比如我想查找<C-x> <C-f>的实现函数和具体实现,只需要<C-h> k <C-x> <C-f>就可以了。

在可查的历史里,我曾经尝试N次从vim到emacs的转变,但是最终都以失败告终。

后来,一个偶然的机会,遇到spacemacs,让我再一次经历了转变失败的过程。

spacemacs是啥

不要问我是啥偶然的机会,因为我自己也已经记不得了。

spacemacs的官网是http://spacemacs.org/,通过作者的精心包装,从命名到完善的文档,使得它看上去更像一个完整的emacs发行版。但是其它,它的本质是另一份emacs配置。

是的,它是另一份emacs配置,就像https://github.com/上其它人托管的.emacs一样。

官方对它的介绍是,”A community-driven Emacs distribution”,一个社区驱动的emacs发行版。

spacemacs为什么这么吸引我

如果你也是一个vim重度用户,并尝试从vim转到emacs阵营,你就会像我一样首先被它的操作模式吸引。spacemacs默认集成了evil,使得在操作习惯上像是一个lisp配置的vim。

当然,对我来说它还有另一个致命的吸引力,那就是华丽丽的外表。默认是一个紫色的本色方案(基佬紫),非常漂亮美观。可以看下面官方的图片:

ss2

下面说一下官方宣传的优点:

我们知道使用emacs的用户,有一些患上了RSI,spacemacs通过集成evil,并将<leader>绑定到space来解决这个问题。由于space键主要使用拇指按压,所以大大减轻了手指的负担。

spacemacs通过使用助词符前缀,将各个功能对应绑定到各个按键。比如buffer操作绑定到b,help绑定到h。也就是说,buffer对应的操作按键是<space> b ...,并且每一个按键都会有功能提示,交互性也得到了一定保证。可以参考图片。整体上很连贯一致。

spacemacs引入了一个layer的概念,使用某一个功能实际上是添加某个layer,这样就把具体的功能跟第三方包解耦,我们只需要知道自己想要的功能,而不需要知道这个功能需要安装哪些包。这可以说是脱离折腾苦海的一个绝妙的办法,把折腾的成本交给社区。

spacemacs默认集成了100多个包,从编辑到cvs管理,功能涉及到方方页面,几乎将emacs包装成了一个简单易用且全面的系统。而且会根据打开的文件类型动态提醒添加的包,完成一站式体验(类似vscode等的包安装提醒)。

当然,还有一些其它的优势,让我在emacs的体验上走得更远。整整试用了两天时间。

两天的体验之旅

在使用spacemacs的这两天里,我发现了很多更优秀的工作流。

举个例子,项目内的批量替换,spacemacs使用插件使得整个过程更加流畅,首先项目搜索某个关键字,然后在交互环境里整体替换就可以了。

当然,有一个难以逃避的弊端,应该归给emacs的设计。那就是buffer的管理相当混乱,甚至使用git都要打开n个buffer,导致有很大的成本耗在查找和切换buffer上。

关于emacs的缺点,可以参考https://www.v2ex.com/t/332566,个人感觉总结的很到位。

放弃spacemacs

最终我决定放弃spacemacs,我发现它并不是我要找的圣杯。

首先,它过于臃肿。我指的臃肿并不是指其他人说的反应慢,而是它有太多不重要的功能,占用了过多的键位,导致看似设计优良的<space>操作方式,一个简单的功能都藏到很深的地方,比如describe-function被绑定到了SPC h d f,甚至很多时候都记不得某个功能对应的按键序列,需要根据交互提醒找到对应按键。当然,这些按键都是可以重新定义的,但是由于设计它耗费了社区很大精力,修改这样的默认按键就显得更加吃力。不知道修改后它还能不能叫做spacemacs了。

其次,之前谈到的,buffer过多难以维护,严重影响思维逻辑。

接下来

你现在看到的这篇文章,是我用emacs写成的。没错,我成功的转到了emacs,使用着我之前使用的vim键位绑定,甚至有一些比以前更好的解决方案。这要归功于spacemacs,它让我看到了自己之前存在的问题。

spacemacs还是会有大批人在使用的,这印证了一个事实,一个高度可配置的编辑器,仍然可以存在一份可以供一群人使用的通用配置。但这并不能发挥可高度定制这个杀手锏,甚至会阻碍你做过多的配置。

正如我使用vim的经历一样,现在这份emacs配置是我自己从零开始配置的,这是完全属于我自己的配置,并且不能被其它人使用。

而且,转换的成功有赖于我另一个努力,那就是努力不使用过多的功能。emacs的功能多到眼花,可能有一些功能这辈子都用不到,而且还会创建过多buffer,导致陷入之前的场景,没法继续使用下去。只使用自己需要的部分功能,就可以熟悉它对buffer的操作,让整个使用过程不会膨胀到自己的控制之外。

就写这么多吧,思维逻辑比较混乱,本来这件事也有点混乱,刚好了。