跳至主要内容

用C语言写解释器(五)

用C语言写解释器(五)——其他一些东西

redraiment, 2009-11-05

写完解释器之后





  这一篇文章我只想和大家侃侃编程语言的事情,不会被放到书中。因此可以天南地北地扯淡,不用像前几篇一样畏首畏尾的了。
  经过前面几篇文章的讨论,已经把用纯 C 语言来实现一个解释器的方法介绍完了。但那些是写给我校 C 语言初学者看的,并不只是你,我得也觉得很不过瘾 ^_^。因此准备继续深入学习编译原理等课程,希望有志同道合的朋友和我一起交流!

富饶的语言(工具)

  在前几篇文章中一直在鼓吹我拍脑袋想出的语言四大要素:“内存管理”、“表达式求值”、“输入/输出”、“按条件跳转”,在这篇文章中您就姑且信一回当它是真的。按照这四条准则去匹配,汇编语言是完全符合的。那为什么又需要 C 语言、Java、C# 等高级语言?这是因为编程除了需要“语言”之外还需要“抽象”!
  “抽象”是个很有效的工具,相信你在为别人介绍自己房间时不会具体到每个木纤维、油漆分子和铁原子。同样的,我们也不乐意总是写一堆 JNZ、JMP 指令,而仅仅是为了实现 if、for、while 等控制结构。C 语言等高级语言提供的抽象的层次更高、表现力更强,允许用更少的语句描述更多的操作。感谢如此富饶的语言为我们带来不同的视角去审视这个世界。
  高级语言相较于低级语言属于更高地抽象层次,高级语言之间的差别主要体现在适用范围上。比如一些语言适合写 WEB 程序,另一些适合做数值分析等。术业有专攻,你只需根据自己的问题来选择一门合适的语言。

什么时候需要创造新的语言

  当我们碰到一类新的问题时,首先考虑的就是定义新的数据结构,并设计多个函数去操作它,最后将它们独立出来打包成一个类库方便在其他地方调用(比如处理图形图像的 OpenGL 库)。上面已经提过,每种语言都有它适合的领域,强行将一门语言用在它不擅长的领域中就出现冗长、繁琐的代码。自然语言也是如此:英语中有种语法叫虚拟语气,描述的是一种假设,并非事实。比如“If I have time, I will go to see you. ”。如果按原意一字不差地翻译相信会很繁琐,我知道台湾作家痞子蔡在使用中文式的虚拟语气很有一套:
如果我还能活一天,
我就要做你的爱侣。
我能活一天吗?可惜。
所以我不是你的爱侣。  ——《第一次亲密接触》
  上面是一段完整表现虚拟语气精髓的话,相信在生活中我们不会这么罗嗦。同样的,如果你发现用现有语言来描述某个特定领域问题时显得力不从心,就可以考虑为这个领域定制一种特定的语言了(Domain Specific Language)!使用现成的词法分析器和语法分析器(比如 lex 和 yacc)对提高开发效率很有帮助,但你也可以考虑采用像 REBOL 这样的语言设计一个“方言”,这会更简单。如果你对 DSL 或 REBOL 有兴趣,可以加入阿里旺旺 REBOL 群(16626148)和蔡学镛前辈交流,他是这方面的专家。

从语言(工具)中挣脱

  从写解释器这件事中可以获得一些建议:不要再争论哪个语言更优秀,只有最适合的;用高级语言写代码首先力求可读性好。第一条建议我在以前讨论“工具理论”时提过很多次,就不再重复,主要交流一下可读性的问题。
  经过了上面冗长的解释和亲自实现解释器以后,大家应该能了解到:一门新语言诞生的动机多数情况下不是为了提高执行效率,而是为了提高开发效率。很多人都沉浸在“++i”比“i++”高效、“10>>1”比“10/2”快等奇技淫巧中,但在你自己实现过解释器后希望能明白,如果真有哪个解释器执行语句“i++;”的效率比“++i;”低,那只能说明这个解释器写得烂!像现代的 C 语言编译器都会有优化的选项,编译时去识别一些常见的热点进行优化,难保那些自以为是的优化反而将代码破坏得连编译器也无法识别。所以要迁就解释器而将代码改得乱七八糟,我宁可换一个更好的解释器!
  真的想深入研究算法,就势必会和硬件相关。你需要精确地知道代码一共执行了多少个时钟周期,而不是简单地根据嵌套了几层 FOR 循环来判断复杂度是 O(n) 还是 O(n2)。除非你深入了解你的解释器,否则无从知晓执行一条 FOR 语句时解释器会不会背着你扫描了整个内存空间。无怪乎经典巨著《计算机程序设计艺术》三卷本中要使用汇编语言来编写代码。

总结

  废话了这么多,我只是想表达“我们是主人”,不要被一个蹩脚的工具牵着鼻子走。当你发现打字员平均打字速度忙时,总不会为了迁就她而只说一些她打得快的字吧?以上内容属于个人观点,切莫认真。欢迎大家通过邮件和我交流你们的想法,我的邮箱地址:redraiment@gmail.com


评论

此博客中的热门博文

AutoHotKey 新手入门教程

AutoHotKey 真是一个好玩的工具!短短几行代码就是先了“窗口置顶”、“窗口透明”等功能,之前我还特意为此装了好几个小工具,现在都可以卸掉了。闲来无事,就把 Quick Start 翻译了一下,我没有逐字逐句地翻译,有时候我嫌原文罗嗦就用自己的话概括地描述了一下。 原文地址:http://www.autohotkey.com/docs/Tutorial.htm 教程目录 创建脚本 启动程序 模拟鼠标键盘 操纵窗口 输入 变量与剪切板 循环 操纵文件 其他特性 创建脚本 每个脚本都是一个纯文本文件,由一些能被 AutoHotKey.exe 执行的命令组成。一个脚本可能还包含 热键 和 热字符串 。如果没有热键和热字符串,脚本在启动的时候就会从头依次执行到尾。 创建一个新的脚本: 下载 并安装 AutoHotkey。 右击鼠标,选择 新建 -> 文本文档 。 输入文件名并确保以 .ahk 结尾。例如:Test.ahk。 右击文件,选择 编辑脚本 。 输入以下内容:#space::Run www.google.com 上一行的第一个字符 "#" 代表键盘上的 Windows 键;所以 #space 表示在按住 Windows 键后再按空格键。"::" 后面的命令会在热键激活后执行,在本例中则会打开谷歌主页。继续按下面步骤操作,来执行这个脚本: 保存并关闭该文件。 双击该文件来启动它。在系统托盘里会出现一个新图标。 按下 Windows 和空格键,网页会在默认的浏览器里打开。 右击系统托盘里的绿色图标可以退出或编辑当前脚本。 注意: 可以同时启动多个脚本,并且在系统托盘里都会有一个相应的图标。 每个脚本都能定义多个 热键 和 热字符串 。 想让某个脚本开机即启动,可以将它的 快捷方式放到开始菜单的启动目录里 。 启动程序 命令 Run 可以运行程序、打开文档、网页链接或快捷键。请参看以下示例: Run Notepad Run C:\My Documents\Address List.doc Run C:\My Documents\My Shortcut.lnk Run www.yahoo.com Run mailto:someone@somedoma

sed单行脚本学习笔记

Sed单行脚本学习笔记 redraiment, 2009-12-31 回家真好   前段时间忙着找工作、项目结题、写报告……反正是总有做不完的事情,哈哈。好在暂时告一段落了,应老妈强烈要求回家休息几天。这次回家除了这身衣服,只带了一本《 sed与awk 》,我觉得这种小册子最适合茶余饭后休闲之用。如果你也有兴趣学 sed ,推荐你一起看《 sed与awk 》(可以在 谷歌图书 在线阅读英文版:D)。   花了两天时间,看完了前面 sed 的部分。要掌握一个工具就要熟悉它的规则,man 等参考手册向我们介绍这些规则,教程则演示如何使用这些规则,但要将这些规则运用自如,还需要去理解别人的代码并尝试自己解决问题。在 SourceForge 上有份经典的文档:《 SED单行脚本快速参考 》(单行脚本要求命令行长度小于65个字符),由 Eric Pement 整理, Joe Hong 翻译,通篇阅读后获益良多,故撰此文和大家分享。 精彩脚本摘录 # 在每一行后面增加一空行 sed G   在参考手册中,命令G的作用是“将换行符后的保持空间内容追加到模式空间”。就像前文提到的,看过教程后只是熟悉了规则,还不能将规则运用自如,我自己写的代码是:sed 's/$/\n/',就是因为我还不熟悉每个命令会对模式空间产生什么影响。所以看到这段参考代码时感觉眼前一亮:“原来还可以这样写!” # 显示文件中的最后10行 (模拟“tail”) sed -e :a -e '$q;N;11,$D;ba'   假设文件有 N 行(N 大于10),显示最后10行也就意味着删除前的 N-10 行。在多行模式中,命令“D”可以删除模式空间中第一行;命令“N”可以将下一行追加到模式空间中,建立多行模式。因此问题转化为:“1)将整个文件的内容放入一个模式空间中;2)删除前 N-10 行。”其中问题1)通过控制语句“b”来解决:sed ':a; N; ba';至于问题二,模式“1,$”代表文本中的所有行,因此紧跟着的命令被执行N次,同理,模式“11,$”匹配后面的 N-10 行,因此“11,$D”一个执行了 N-10 次。   其实,在 GNU sed 中,命令“$q”是可以删掉的,因为在最后一行执行命令“N”就会因出错而自动退出。   另外,在

Linux下用nc实现DuplexPipe

Linux下用nc实现DuplexPipe redraiment, 2010-01-25   nc 是一把网络的瑞士军刀,我以前在介绍 DuplexPipe 时也提到过,如果你没接触过它,可以先参看一下《 DuplexPipe二三事(二) 》。再来简单地介绍一下 DuplexPipe,顾名思义,它是一个“双向管道”。在 shell 中,我们通过“|”使用匿名管道,让前一条命令的输出作为后一条命令的输入;双向管道即在此基础上在加上“后一条命令的输入作为前一条命令的输入”。这是最初开发它的原因,但后来发现它更像是一个网络接口转换器,“DuplexPipe”这个名字反而不能体现它的功能。更多内容请参看 DuplexPipe系列文章 。 留言   今天网友 黄海 给我留言,他通过用 nc 的 -e 选项来执行 nc 本身来实现 DuplexPipe。留言原文如下: 哥们,你写的那个DuplexPipe, 我很欣赏。不过近日于网上逛发现此工具的功能竟然完全可以用netcat做到,有两种方法,我的博客上载了一种。简单描述如下: 在windows下: echo nc [ip] [port] > relay.bat nc -l -p [port2] -e relay.bat 其余的类推 第二种方法是用命名管道:(linux下) mknod backpipe p nc -l -p [port] 0<backpipe | nc [ip] [port12] | tee backpipe   其中选项 -e 的作用是: for NT:    -e prog        inbound program to exec [dangerous!!] for Linux: -e filename    program to exec after connect [dangerous!!] Windows下不行   在我开发 DuplexPipe 时确实考虑过功能会不会和 nc 重叠,当时只想着通过 shell 管道来连接,忘了 nc 自带了一个双向管道!我首先在 Vista 下做了测试,nc(win32) 是从 http://www.securityfocus.com/tools/139 下载。开启三个命令提示符,分别执行: 1) nc -l -p 1234 2)