﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-huaxiazhihuo-随笔分类-emacs elisp</title><link>http://www.cppblog.com/huaxiazhihuo/category/21216.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 20 May 2016 03:21:41 GMT</lastBuildDate><pubDate>Fri, 20 May 2016 03:21:41 GMT</pubDate><ttl>60</ttl><item><title>lisp的括号</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/20/213552.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Fri, 20 May 2016 03:17:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/20/213552.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/213552.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/20/213552.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/213552.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/213552.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lisp(当然也包括scheme)的元编程(也即是宏)威力非常强悍，相比之下，c++的元编程(template+预处理)简直就是弱爆了，被人家甩几条街都不止。 当然，template的类型推导很厉害，也能生成很多签名类似的class和function，比其他语言的泛型强多了，但是，template再厉害，也不能生成名字相似的function还有变量。 预处理可以生成名字相似的变量和函数。但是，预处理的图灵完备是没有类型这个概念，只有字符串，整数那个东西还要靠字符串的并接来实现。所以，预处理没法得到template里面的类型信息。新版本的c++中有了decltype之后，宏可以通过某种方式以统一的形式来利用类型信息。但是,在代码生成方面，预处理还是很弱智，主要的问题在于宏对于自己要生成的代码结构很难构建语法树，也不能利用编译阶段的功能，比如调用编译阶段的函数。 想说的是，很难以在代码中只用宏来写一个稍微复杂的程序，即使做得到，也要吐好几口老血，还煞难调试。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lisp就不一样了，宏和语言融为一体，以至于代码即是数据。只要你愿意，完全可以在lisp中只用宏写代码，只要愿意，分钟钟可以用lisp写一个dsl，比如loop就是一个专门处理循环的dsl。甚至，用lisp宏还可以做静态类型推导的事情，也非难事。因此，用lisp宏搞基于对象 (adt)也都有可能，从而优雅的使用.操作符。比如(+ obj1.item obj2.item) (obj.fun 2 "hello")。你说，lisp宏连.操作符都可以做到，就问你怕不怕。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是，宏再厉害，也不能随意地搞底层操作内存。恰好与c++相反，c++搞底层随意操作内存太容易了，但是元编程的能力就远远不如lisp了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; emacs是最好玩的ide，注意不是最强大，猿猴随时可以写代码增加改变emacs的功能，马上见效，不需要任何配置，不需要重启。因此，elisp也是最好玩的语言了，因为最好玩的ide的脚本语言就是它了，呵呵，主要原因还是elisp是lisp的方言，可以承担lisp的很多构思，当然，完全继承是不行的，不过，已经足够做很多很多的事情了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不过，本期的话题是lisp的括号，为什么lisp会有那么多的括号，铺天盖地，很容易，就一堆一堆的括号扰人耳目，以至于lisp代码不好手写，只能忽视括号，依靠缩进。括号表示嵌套，相必之下，c系语言的嵌套就没那么恐怖了。一个程序，顶破天，最深层都不会超过十层，连同名字空间，类声明，函数，再到内部的for，if，大括号，中括号，小括号。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中缀表达式，这个众所周知了，试比较，1+2-3*4/obj.width，没有任何括号，依靠运算符优先级表示层次关系。并且，猿猴也习惯并本能的解析中缀表达式了，因此，代码看起来一目了然。lisp就很可怜了， (- (+ 1 2) (/ (* 3 4) (obj-width obj)))，这里面多了多少括号，在转换成这行简单算式的时候，还是在emacs下面写出来的。关键是，虽然前缀表达式没有任何运算级别上的歧义，但是，人眼还是比较习惯中缀表达式了。君不见haskell的括号更少了，其对中缀表达式和符号的运用更深入。关键是，中缀表达式很容易手写啊。易写，自然也表示易读。C#的linq的深受欢迎也因为其好读，无须在大脑里面建立什么堆栈，linq表达式就是上一个处理的结果通过.操作符传递到下一个运算中，非常顺畅，不必返回前面去看看当前的操作数的运算是什么，因为运算符就在眼前了。中缀表达式.操作符，更是灭掉括号的大杀器，比如，obj.child1.child2.value，这里用lisp来搞，4个括号避免不了的。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 试试将java万物皆是对象推向极致，然后没有中缀表达式，1.plus(2).minus(3.mult(4).obj.width))，比lisp要好一些，但也有很多括号了，并且，在minus这里，其括号嵌套也只是减少了一层而已。当表达式复杂起来的时候，这种缺点也要相应的放大。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 变量的就地定义，好像c系的变量要用到的时候才定义这种语法很稀疏平常，没什么了不起的。但是，到了lisp下面时，就知道这是多么贴近人心的便利啊。每次用到新变量，都要引入let表达式，又或许跑到前面的let语句中写变量，要么就打断当前的代码编写，要么就引入新的一层嵌套关系。一个状态复杂的函数，很容易就出现好多个let语法块。而c系的变量就地定义，显得那么淡定。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return，continue，break等语句就可以把后面的语句拉起来一个层次，假设没有这些关键字，要用if else语句，那么，这些return，continue，break后面的语句都表示要被包含在一个else的大括号中。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lisp里面的特有语句，with-*等宏，都要求嵌套。几个with-*宏串起来，几个括号嵌套关系就跑不了啦。而c++通过析构函数就多么地让人爱不释手了，java也可以别扭的用finally来应付了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 控制结构的并行。像是if，for，while或者是class还有函数定义等语句，其后面的代码块是并列在关键字的后面，这样就少了一层嵌套。不过这个作用并没有那么巨大。主要还是前面4点。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样，就可以模拟其他语言的特性来灭掉lisp的括号。当然是要到宏了，loop就是一种尝试。但是，下面将走得更远。其实，就是设计一套新的语法了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假设这个宏的名字是$block，那么后面的文章就可以这样做。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1 加入一个$操作符。$表示后面的代码都被收入进去。比如，1+2-3*4/4，就可以写成($block (-) $ (+ 1 2) (/) $ (* 3 4) 4)。于是，with-*等宏的嵌套就可以用$来代替了。虽然，$的作用好像有些欠缺，功能不完备，但是，只要考虑到括号都是在最外层体现的，那么，$就显得很有作用了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2 加入let的操作符，表示就地引入变量，其实也即是将变量名字加入上层的(let)的变量列表中，然后在这里插入一条(setq var vlaue)的语句。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3 加入if，elif，else，for，switch等语句，于是后面的代码块就与之平行了，并且准备一个(let)的语句，用于给with语句添加变量。可以借鉴loop宏的方式<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4 return，break，continue等相应的实现。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5 支持.操作符，所有关于.的操作，都转换成相应的函数操作，好像以前的cfront在对于成员函数的支持那样子。这里就要有静态类型推导了，可以通过with语句中加入变量的类型说明，给函数添加返回类型的标签。有了这些信息，就可以找到obj相应的成员函数的名字，就可以生成对应的函数调用的form了，这个做起来有点难度。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ......<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以上，除了第5点，其他都可以借鉴loop的代码来实现。$block里面的代码，便于手写，括号也没有那么面目可憎了。<br /></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/213552.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2016-05-20 11:17 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2016/05/20/213552.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>