- 论坛徽章:
- 2
|
本帖最后由 OwnWaterloo 于 2011-08-19 01:06 编辑
回复 140# pandaiam
要传奇色彩, 要忽悠什么的, 找sw…… 他比较八卦……
我觉得lisp最吸引人的就是: lisp的代码不仅仅是代码, 这些代码在读入之后, 是可由程序员操作的数据结构。
其他语言解析后肯定也有类似的解析结果, 但只会有限地暴露给程序员。
反正我是将这条标准作为是否是lisp方言的分界线。
因为程序是由数据结构表现, 而且数据结构不隐藏, 所以元编程能力与其他语言相比就不是一个层次上的东西。
举点例子吧。
C++编译器是肯定有实现typeof的, 因为sizeof首先需要typeof。
但C++03就是不将这一信息暴露给程序员。 然后就出现了各种猥琐且带有各种限制的typeof模拟……
直到C++0x,这一信息才终于暴露给程序员使用了。
lisp 就不会出现这种事情。 因为我只有elisp的编程经验, 所以只说elisp。
elisp不像C++那样可以自定义类型, 所以typeof只对内建的几种类型有效, 对比意义不大。
即使如此, elisp需要自定义类型时, 是这样模拟的:
(keymap ...) 这就是个keymap, (0 0 [nil nil nil ...]) 就可以当作一个环形buffer使用。
也就是说, 这些信息对程序员是充分开放的。
而C++03里面搞死人的函数签名, 在lisp里面也是开放的:
(lambda (argument list) ... )
获取表的第2个元素就行了。
C++03里面, 要怎么获取一个函数类型? 返回值是什么? 每个参数分别是什么类型?
写个最简化的:
- template<typename F>
- struct signature;
- template<typename R>
- struct signature<R()> {
- typedef R R;
- };
- template<typename R,typename P0>
- struct signature<R(P0)> {
- typedef R R;
- typedef P0 P0;
- };
- ...
复制代码 这完全就是体力活。 没有任何有效的技术能避免这种体力劳动, 除了直接扩展为文本的预处理器, 也就是宏。
boost有个pp库, 专门干这个事情, 但因为C/C++预处理器不是编程语言, 肯定就有很多限制, 比如循环结构根本就是假的……
无论如何, 这已经能再次体现其他语言与lisp元编程的不同了, 其他语言元编程的结果是程序文本, 而lisp的结果是数据结构。
这还没完, 即使获取了函数的签名信息, 想转发一个函数依然没有有效的技术。 又得boost pp干活…… 恶心到死……
直到C++0x引入了可变长模板, 这两问题才真正解决了……
而lisp里面, 想要转发一个函数 (fset 'f (lambda (a b) ...)) 简直就太容易了……
1. 获取参数列表(第2项) (a b)
2. 将这个参数列表作为新函数的参数列表。 新函数也是一个列表, 第1项是lambda, 第2项就是旧函数的参数列表
(lambda (a b)
3. "构造"转发"语句" 也是在操作列表, 第1项是函数名, 后面是参数:
(lambda (a b)
(f a b)
4. 其他地方就任意发挥了, 想添加什么就添加什么。
(lambda (a b)
...
(f a b)
... )
再也不用等着语言的下一个版本将那些不知道是因为什么原因而隐藏的元数据暴露给你。
再也不用等待语言的下一个版本添加新的特性。
C++0x等了多少年? 虽然是投票通过了, 还要等着发布, 还要等着编译器实现……
lisp里, 想要的数据, 想要的特性, 随时可以动手做……
而且, lisp另一个特点: 这东西真心不难啊…… 语言本身简洁且一致。
除了将那些蛋疼的<></> < > 替换成括号, 这语法与xml有什么区别啊?
顺带还能执行, 而且50年代就出现了, 所以我才说sgml/html/xml都算逆发明, 反发明, 反文明。
语言的核心概念少得可怜(elisp info里面大部分都是与edit相关的东西):
1. 有个read过程, 将代码转换为数据, 然后是求值 eval
1.1 代码如何转换为数据由 read syntax 定义 —— 见elisp info
1.2 还有一些特殊的marker 比如 'a 读入后的结果是 (quote a) —— 见elisp info
2. 接下来是求值eval步骤: 所有数据求值方式都相同 —— 返回自身 —— 除了symbol与list(真正术语叫cons)。
3. symbol的求值结果就是 symbol 的 value cell —— 除了 symbol 是 list 第1项的情况外
3.1 symbol 在list的第1项时, 是取 function cell
实际的完整情况还要细分, 因为elisp实际上并不对list第1项求值 —— 见elisp info
3.2 但也有其他lisp表求值时第1项的求值方式与其他地方是相同的, 而且也没有 function/value cell之分, 比如scheme。
3.3 elisp 还有两类特殊的 const symbol, value cell 的值不会被改变。
一类的value cell 是symbol自身: t nil 以及所有冒号开头的 symbol
还有一类是语言常量, most-positive-fixnum 什么的。
4. list求值类似与函数调用, 第1项是函数, 求值方式如上所说, 见elisp info
4.1 其余项是参数, 依次求值并将结果作为实际参数传入 —— 除了特殊表与宏。
4.2 如果是特殊表, 不求值, 而是将read后的结果直接作为实际参数传入 —— 完整的特殊表列表见 elisp info
4.3 如果是宏, 也不对参数求值, 直接传递。
4.3.1 宏是这样一个东西: (macro (lambda (arg...))
4.3.2 表第1项是macro符号, 第2项是一个函数。
4.4.3 宏调用实际是将read的未求值的结果传递给后面那个函数, 并对该函数计算得到的结果再次求值
over…… 语言就这么几条规则,只有几条例外但又必不可少的规则, 只有几处需要继续看elisp info获得完整信息。
千万不要乱找书看, 尤其是elisp的书。
大多数用elisp的都是程序员, 都是有其他语言的经验, 然后就套用在elisp上 —— 结果只能是胡扯。
比如programming in emacs lisp是把我害惨了的…… 几乎就是elisp界的谭浩强了…… |
|