您的位置:首页 > 编程语言

『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*

2017-01-11 00:00 155 查看
『阿男的编程本质论』*08 Eval,Macro,Preprocessor,Homoiconicity(一)*

阿男这次想跟大家聊聊语言设计里面的几个概念,分别是Eval,Macro,Preprocessor和Homoiconicity。

首先是
Eval
,我们在很多语言里都可以看到它的影子,比如
Python
Perl
Ruby
还有
Javascript
,等等。以Ruby为例,下面是在Ruby里面使用
eval
的例子:

irb(main):001:0> eval "1+1"
=> 2

从上面的例子我们可以看到,
eval
的用处就是接受一段字符串作为代码,并且执行这个代码。就像上面这样,我们的字符串是
1+1
,使用
eval
作用在这个字符串上面,它就作为了Ruby里面的代码来执行了,然后给出结果为
2


Perl和Python还有Javascript都是差不多的使用方法。我们可以想一想,似乎Java语言,C语言里面并没有
eval
这种功能,为什么?

我们得想一想
eval
是怎么实现的,把字符串作为代码,等于就是在程序Runtime的时候,这个字符串可以「编译」成代码。因此,也就是说这门语言需要具备Compile-at-runtime的能力。

C语言或者Java语言这种,是明确区分Compile Phase和Runtime Phase的。我们的C语言代码,需要被编译器先编译成汇编代码,变成一个可执行文件的。而我们使用的,是编译后的可执行文件,因此我们不可能让编译后的代码,去解析一个字符串,再把它变成汇编代码。

Java的编译过程也是和运行时互相独立的,需要把Java文件编译成class文件,class文件里面有bytecode,在JVM上面执行。

当然,Java发展到现在,也支持Compile-at-runtime,这种技术有个名字,叫做
JIT
,也就是
Just-In-Time
Compiler。有了这个能力,就允许代码在运行时被实时地编译,然后虚拟机里面的class文件在运行时被实时地加载。

但是光有Compile-at-runtime还不够,我们还得看这门语言自身的语言特性是否支持runtime时代码自身的改变。Java在设计的时候,对class之间的依赖关系,package之间的关系等等,都有很多约定,可以说Java是一门设计的比较
"严格"的语言,所以实现
eval
是有难度的。

因此,现在有很多语言,是架构在JVM平台上,最终也是compile成bytecode,但是设计的比Java语言更灵活一些,因此就可以实现
eval
的功能,比如Groovy,还有后续要讲到的Clojure。我们可以看看Groovy的eval的代码例子:

$ groovysh
Groovy Shell (2.4.7, JVM: 1.8.0_112)
Type ':help' or ':h' for help.
-------------------------------------------------------------------------------
groovy:000> Eval.me('2 * 4 + 2')
===> 10

从上面的例子,我们可以看到Groovy的
Eval
的使用方法。接下来我们看Ruby的
eval


$ irb
irb(main):001:0> eval "1+1" => 2

Ruby也有自己的虚拟机和virtual machine code叫做YARV,阿男之前给大家讲过了。因为Ruby也支持Compile-at-runtime,所以实现
eval
是没问题的。

Python,Perl和Javascript也是一样,所以我们要明白的是,如果一门语言在语言设计和虚拟机设计上支持Compile-at-runtime,那么这门语言就可以在运行时,把一串字符编译成代码并且加载执行,也就是实现了
eval
的功能。

这种功能固然很方便很强大,但是它有什么问题没有?

其实语言设计里面,并不存在绝对的优势,有的功能,它的优点同时也就是它的缺点,比如
eval
这种功能就是一个典型的例子。因为我们可以把代码封装在字符串里,然后在运行时编译并执行,那么如果这个字符串里面的代码有问题,这个问题会在运行时才发现。

因此Compile-at-runtime虽然很灵活,但是compile和runtime混在一起的话,也就意味着很多代码的问题会在运行时才能发现。而C语言或者Java,很多问题会在明确的compile这一步被发现,而不是runtime时才被发现。

最后我们要说一下,以上的讨论都是基于这一门语言有compile这一步骤而讨论的,其实还有更简单的实现,就是Interpreter。所谓Interpreter,就是指直接解析文本,然后根据文本直接执行命令,并没有把源代码转化成目标代码的过程,也没有用来运行目标代码的虚拟机。比如早期的Ruby就是使用的这种形式,提供一堆用C语言编写的API接口,然后Ruby代码就直接解析,然后调用这些接口来执行代码。

Interpreter的执行效率和功能上面都有局限,所以Ruby从1.9开始就转为使用虚拟机加自己的虚拟机YARV code的形式来执行代码,这一点和JVM平台越来越像。可以说,VM加VM code是主流趋势。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息