Lisp与JAVA的酷毙结合——abcl

  最近看了一本叫做《黑客与画家》的书,其中对于Lisp语言大加褒奖。自己试着用了一下,虽然确实有反人类之嫌,但是确实是一门不错的语言,New Architect杂志上有一篇介绍ITA软件公司的文章,里面说"一行Lisp代码相当于20行C代码"。Lisp的强大,从中就可见一斑了。另外,Lisp还有理论上最高的计算能力。还有一个笑话是这么讲的:“任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的Common Lisp实现。"

     但是Lisp是一种解释性语言,不能直接执行。所以,将其嵌入一个稳定的框架是个不错的选择。Java与Lisp就可以这样整合,其接口有很多,但我认为abcl(Armed Bear Common Lisp)是最好的选择,这个接口的性能表现是我觉得最优的。与其说它是一个接口,不如说是一个JAVA上的Common Lisp实现。本文将分两部分讲述,其一是怎么在JAVA里调用Lisp,其二是怎么在Lisp里调用JAVA,这有助于提高Lisp的扩展性。

Part One 在JAVA里用Lisp:

  首先要安装abcl。在abcl官网下载其binary版本。解压出abcl.jar后,把它移动到jdk\lib目录下。并在环境变量CLASSPATH后加上一段话:%JAVA_HOME%\lib\abcl.jar(假设JAVA_HOME已经设置,指向jdk目录)。这样,abcl就算是安装好了。(与那些需要编译的C++库,安装过程简直太简单了,--我不是在吐槽boost!)

      安装完毕后,就可以使用此接口了。在JAVA里用Lisp时,通常不把Lisp源码硬编码在JAVA程序中,而是另外建一个.lisp文件,在其中写上一些函数。这样易于修改,也不会致使你的JAVA源代码又臭又长!(想象一下,把Lisp源码以行为分割,打在方法的参数里,程序实在是太美观了)要加载外部的Lisp程序,可以这么做:

    Interpreter interpreter = Interpreter.createInstance();

    interpreter.eval("(load \"my-lisp-code.lisp\")");

  “my-lisp-code.lisp”是外部lisp文件的文件名。那么加载后,怎么调用这段Lisp代码中的程序呢?首先,我们要得到一个abcl的Package:

    org.armedbear.lisp.Package defaultPackage = Packages.findPackage("CL-USER");

  在这个package里,我们要寻找Lisp代码中的函数,假设该函数叫做lispfunction,那么,代码应该这样写:

    Symbol myFunctionSym=defaultPackage.findAccessibleSymbol("LISPFUNCTION");

  根据这个Symbol对象,我们可以创建一个Fucntion对象并且运行,这里假设目标函数没有输入参数:

    Function myFunction=(Function)myFunctionSym.getSymbolFunction();
    myFunction.execute();

  如果有参数呢?只要在execute中类似正常函数传参的方式,按次序写入参数即可。但要注意的是,不能直接填写,而要new一个JavaObject后传入,且Lisp代码中要用(jobject-lisp-value x)来转换。

  如果又返回值呢?execute本来返回一个LispObject,可以通过其各种成员方法转换成相应的类型。

  下面给出一个官方提供的例子,实现两个数的加法运算:点此下载

Part Two 在Lisp里用JAVA:

  第一步依然是安装。提前声明:而且本部分的内容基于Part One。

  通常的实现模式是按上面的方法在JAVA里调用Lisp,并且传入一个类。在Lisp里,通过一系列的转换,然后就可以调用了。

  这里主要讲Lisp里的代码实现,JAVA部分只要写好函数,然后还需要在调用过程里这么写:

    myFunction.execute(New Class1());

  这就是传入的参数了,是一个类的实例。例如这个类有一个成员函数:addTwoNumbers(a,b)——表示两个数相加的和,a与b均为int类型,返回值也是int类型。传入后,在Lisp部分应该这么写(传入的参数为param):

  (let* ((class (jclass "Main"))
	 (intclass (jclass "int"))
	 (method (jmethod class "addTwoNumbers" intclass intclass))
	 (result (jcall method param 2 4)))
    (format t "in void-function, result of calling addTwoNumbers(2, 4): ~a~%" result)))

  其实很简单,在使用时,可以遵循此模式,先转换JAVA中的类型,再计算。

  给出官方提供的例子代码:点此下载 

附录:

  这里讲些题外话,像abcl的运行速度,其他的Lisp->JAVA接口及如何打开abcl并把其作为一个外部独立运行的实现以学习Lisp的方法.

  速度:许多Lisp用户就是奔着它有着理论上最高的运行速度去的。根据实验测试发现,其运行时间分两部分,分别是预载和实际计算。获取解释器实例(预载)时比较慢,命令行下为5-6s,Eclipse下更长。实际计算时间倒是在可以接受的范围内。

  独立外部实现:下载了abcl.jar并且安装后,可以这样来运行它,将其作为一个外部的解释器——学习Lisp:

    java -jar abcl.jar

  另外的Lisp->JAVA接口现在概括如下:

    1.abcl不支持Android,但目前仍在持续开发中,不排除2.0版本会对Android进行支持。
    2.ecl支持Android,看github上的进度很久没有commit了,所以不建议优先考虑,但是这个接口还支持C语言。
    3.clojure的Android支持性比较好,有nightcode这个集成的IDE,而且比较流行。
    4.mocl,一个收费的common lisp的Android接口,具体性能也无法评定。

后记:

  因为要期中迎考与本人较懒的缘故,本文后两部分写的较为粗略。可能无法从中清晰地了解到我所想讲述的。如果有什么疑问,可以评论或短消息我,我会竭尽所能地帮助。

posted @ 2015-04-17 19:55  Darksun2010  阅读(2950)  评论(1编辑  收藏  举报