得益于 @fly2never的分享,我看到一个有关松本行宏先生当年如何使用 Emacs, 以及如何使用 Emacs 实现 Ruby 的 PPT 演示稿,其中讲了许多我原先不知道的,发生在 Emacs 和 Ruby 之间的故事。
原文链接: http://www.slideshare.net/yukihiro_matz/how-emacs-changed-my-life
我相信不是每个人都愿意去读英文。我就毛遂自荐,为大家翻译了,之所以做这件事情,我想主要是 PPT 的内容引起了我强烈的共鸣的缘故吧。虽然使用 Emacs(以及 Ruby) 的层次和水平我和 Matz 相差比天高,不过,这并无法阻止我发自内心的喜欢 Ruby, 喜欢 Emacs.
事实上,我在给 Ruby 新人的公开信就有提到,我的 Ruby 编程之路,是始于 Emacs 的学习与使用。而且在我个人身上,真真切切的发生了一件和 Matz 非常相似的事情。那就是:在使用 Emacs 之前,接触过很多 IDE, 也曾是 Borland 的忠实粉丝,但是很多年,我一直对编程提不起兴趣或无法坚持,主要原因,应该是我像电影里演的那样,那些黑客的那种噼里啪啦如行云流水一般的感觉。真正的改变,发生在使用 Emacs 以后,找到了一种久违的写字的感觉。我使用 org-mode 记笔记,以及 GTD 整理我的思绪,并乐此不疲的折腾再折腾,说白了就是为了这种传说中的 feeling. 随心所欲的 feeling, 自由的 feeling.
我承认也许 Emacs 只是碰巧在我人生试图发生改变的时候,碰巧发生在了我的面前,但不可否认的是,Emacs 最为个人未来编程生涯的一个起点,而且显而易见,现在我每天做的每件事情,几乎都跟它有关 (离不开它), 从这个意义上来说,Emacs 对我个人而言,正如 matz 那样,一个新的开始,而现在看起来,也是一段美好的回忆。
下面正文开始:
在 1980 年。
我开始编程...
(出现了一个夏普计算器的图像)
400 steps (应该是那个计算器的型号,表示那时候计算机还很初级.)
到了 1988 年。
在一台 SUN-3 型计算机上,我遇到了 Emacs.
同学们之间相互分享这个东西。
我也尝试着使用它。
但是我无法真正使用它。
因为 Emacs 在系统内被禁止使用的。
因为它占用了太多的宝贵内存。(现在绝大多数的程序员应该无法想象当年节省 1kb 内存对于一个程序来说多么重要)
但是我们可以自由下载,因为它是自由软件。
我下载了 Emacs 源码。
开始研究...
Emacs 是我使用过的地一个 Lisp 解释器。
从 Emacs 中,我了解到很多很多有关语言的实现。
embedding integers in pointers (如何在指针的低位嵌入目标类型)
(使用 Mark and Sweep 的方式来处理垃圾收集问题)
C 和 Lisp 之间的调用约定。
我实实在在的懂得了 Lisp 如何工作。
我沉迷于 Lisp 对象,以及如何通过 C 来实现 Lisp.
这一年,我开始使用 Sparc 工作站。
我终于开始使用 Eamcs.
Emacs 变成了我的一部分。
我可以随心所欲的改变任何不喜欢的地方。
Emacs 的所有东西都是完全可配置的。
Emacs, 让我实现了作为一个程序员可以实现的所有一切。
我可以随心所欲的换绑任意按键。
离开 Emacs, 我不想写任何东西。
包括程序,文档还有 Mail.
所以我写了我自己的 Mail Lisp 客户端。
它叫做cmail
. 在 Emacs 下使用。
这是我写的第一个 (不算太小的)Emacs-Lisp 程序。
我每天都在使用它。
到了 1993 年。(地球人都知道,1993 年发生了什么)
我开始了 Ruby 解释器的开发。
受到 Emacs 解释器的影响。
Integers are coded in tagged points (还是不明白,整数被硬编码为 tagged 指针?)
我使用了简单的 mark 以及垃圾收集机制。
我使用了类似于 Lisp 的对象模型。
并借鉴了 Smalltalk 一切都是对象的理念。
在语法上,我借鉴了 Algol, Ada, Eiffel.
但是,作为一个沉溺于 Emacs 的瘾君子,我还需要一个用于编写 Ruby 代码的 mode.
首先,自动缩进必要的。
在 1993 年,还没有针对 Ruby 这类语法 (以 end 作为结束) 的自动缩进模式。
所以,我尝试写了一个 ruby-mode.el
基本上就是处理emacs lisp和正则表达式之类的概念。
整整花了我一个星期...
用尽各种办法,我愣是给实现了代码自动缩进功能。
现在回想起来,如果 (当时) 我无法让 ruby-mode 开始工作。
现在 Ruby 的语法也许会被改变。
也许会变的更像 C 一些。
或者和其他脚本语言更相似。
如果那样做了,我想 Ruby 一定没有现在这么受欢迎。
Emacs 教会了我自由软件精神。
Emacs 教会了我如何读代码。
Emacs 教我领略了 Lisp 的威力。
Emacs 教会了我如何实现一个语言核心。
Emacs 教会了我如何实现垃圾收集。
Emacs 帮助我编码和调试。
Emacs 帮助我写,编辑文本,邮件,文档等等。
Emacs 使我成为一个高效的程序员。
Emacs 使我成为了一个 hacker.
Emacs 改变了我的生活。
永远...
感谢Emacs.
可以在 Emacs 里 M-x term,然后再开个 vim。也可以用 M-x viper-mode
#2 楼 @poshboytl @lgn21st 可以用 evil,emacs 操作系统中的 vim http://gitorious.org/evil/pages/Home
@zw963 当年也有一个兄弟像你一样热情的向我宣传 emacs 的诸般好处 在他反复讲了 N 年后(N>3) ,我才开始使用 emacs 并且一直使用
现在回过头来,觉得学 emacs 真是值
github 我一直都是直接 download zip...还不会用。今天研究研究 git.
水平说实话,非常拙劣。总是想搞的再好一点再传。事实上,现在貌似没什么大问题了。
觉得现在跟以前不同了,那个年代计算机刚刚普及,有的机器指令只有几条,可以尽情 hack。现在别说机器指令了,任何语言,或者一个方向都很深,想要精通都很难
@zw963 好啊,我的 Emacs 配置也差不多重构好了,只是最近有几个插件 (helm) 更新比较频繁,准备自己使用一段时间后再传上去。到时可以互相借鉴学习下
#27 楼 @doitian BTW Yari 可以作为 Anything 和 Heml 的 sources,官方的 Yari 目前只支持 Anything,但因为 Anything 和 Heml 一样的,所以我 Fork 了一份 Yari 并添加了 Heml 支持。 添加
(define-key 'help-command "R" 'yari-helm) 或者你自己绑定的键
效果很不错。
“embedding integers in points“,汗一个,看了原文才知道是 pointers。 其实说的是这样的意思:比如在 c 语言里面经常可以用类似这样的方式描述类型不定的数据:
struct foo {
union {
int simple_data;
void* complex_data;
};
/* ... */
};
即如果数据足够小,就直接存放,否则就放它的指针,实际数据在另外的地方。但是这样就需要知道,现在究竟是哪种情况,直观的做法是另有一个成员表示当前类型:
struct foo {
enum {/*...*/} type;
union {
int simple_data;
void* complex_data;
};
/* ... */
};
由于对齐的原因,这个 type 占用的空间可能多至一个字长,多么浪费啊^^ 不过这里有个取巧的办法,出于效率的考虑,正常分配出来的内存,地址是要求对齐的,即使是 16 位机(应该没有 8 位机能运行 Emacs 吧^^),也是按双字节对齐的,那么正常的指针,最低位的应该是 0,于是就可以把类型信息嵌入到这些位上了。
这是实实在在的诱惑啊,之前一直告诉自己,vim 和 emacs,这两者让人那么难以抉择,说明两者的价值应该是差不多的,那就随便选择一个好了。没有想到,现在对 emacs 又心痒了,真是“无折腾,不生命”…
embedding integers in pointers 中的类型实际上是放在高地址,我查过 emacs 代码,见我的翻译,http://blog.csdn.net/redguardtoo/article/details/7400655.
#39 楼 @u1331878576 Ruby 是 LSB 这个做标志位,标明是整数还是指针,省到 type 所占的位置, @neutralevil 解释的很清楚了 http://media.pragprog.com/titles/ruby3/ext_ruby.pdf 这个 P10 有详细说明
不知道现场怎么谈的,我只有演讲提纲,所以假定谈的都是 emacs 中的实现 (不知道 ruby 如何实现),我又去验证了一遍。至少在 emacs 中的 struct union Lisp_Object 的实现如我所说。
这是 lisp.h 中的代码 (X86 都是 little-endian)
#ifndef WORDS_BIG_ENDIAN
/* Definition of Lisp_Object for little-endian machines. */
typedef
union Lisp_Object
{
/* Used for comparing two Lisp_Objects;
also, positive integers can be accessed fast this way. */
EMACS_UINT i;
struct
{
EMACS_INT val : VALBITS;
enum Lisp_Type type : GCTYPEBITS;
} s;
struct
{
EMACS_UINT val : VALBITS;
enum Lisp_Type type : GCTYPEBITS;
} u;
}
Lisp_Object;
#else /* If WORDS_BIG_ENDIAN */
我的测试代码,使用 gdb 调试,首先 print &(o.s) 得到 memory address,然后参考http://www.delorie.com/gnu/docs/gdb/gdb_56.html一个个字节显示来验证。
#include <stdio.h>
#define EMACS_UINT unsigned int
#define EMACS_INT int
#define VALBITS 29
#define GCTYPEBITS 3
enum Lisp_Type {
Lisp_Int = 0,
/* Symbol. XSYMBOL (object) points to a struct Lisp_Symbol. */
Lisp_Symbol = 2,
/* Miscellaneous. XMISC (object) points to a union Lisp_Misc,
whose first member indicates the subtype. */
Lisp_Misc = 3,
/* String. XSTRING (object) points to a struct Lisp_String.
The length of the string, and its contents, are stored therein. */
Lisp_String = 4,
/* Vector of Lisp objects, or something resembling it.
XVECTOR (object) points to a struct Lisp_Vector, which contains
the size and contents. The size field also contains the type
information, if it's not a real vector object. */
Lisp_Vectorlike = 5,
/* Cons. XCONS (object) points to a struct Lisp_Cons. */
Lisp_Cons = 6,
Lisp_Float = 7,
};
typedef union Lisp_Object
{
/* Used for comparing two Lisp_Objects;
also, positive integers can be accessed fast this way. */
EMACS_UINT i;
struct
{
EMACS_INT val : VALBITS;
enum Lisp_Type type : GCTYPEBITS;
} s;
struct
{
EMACS_UINT val : VALBITS;
enum Lisp_Type type : GCTYPEBITS;
} u;
} Lisp_OLisp_Stringbject;
struct S{
int a;
int b;
};
int main(int argc,char** argv)
{
struct S s;
printf("addr of S::a=%x\naddr of S::b=%x\n",&(s.a),&(s.b));
Lisp_OLisp_Stringbject o;
o.s.val=1023;
o.s.type=Lisp_Int;
printf("o.s.val=%d\no.s.type=%d\n",o.s.val,o.s.type);
return 0;
}
我是先用的 emacs,后来听说了 Vim 又学 Vim,一直用到现在。一直很满意,起码 linux 默认自带 vi。工作经历不太丰富,没见过用 Emacs 写代码的,一个都没有。用 Vim 写代码的倒是不少,起码服务器上写配置都很溜。