Clojure的学习笔记(三)

Clojure和Java

Clojure的代码是被编译为java字节码而执行的,因此Clojure和Java代码的集成度相当高,这也是很多人学习Clojure的原因,因为跟Lisp这样的语言相比,它不仅仅具备强大的表达能力,同时也能尽可能的利用现有的‘智力遗产’。

如何访问构造函数、方法和域

Clojure中提供了new关键字来生成一个对象 (new classname),生成的对象可以用def语句绑定到某个变量之上,绑定之后便可以用 . 运算符来访问该对象的方法:

(def rnd (new java.util.Random))

(. rnd nextInt)
-791474443

对于方法调用的参数,则直接写在函数名后面就可以了:

(. rnd nextInt 10)

. 运算符不仅仅可以用在访问对象的方法,还可以用在访问对象的域、静态的或者非静态的都行。比如我们可以用(. Math PI)访问Math类下的静态变量PI

需要记住的是,clojure的new方法的参数必须是相对PATH的完整的类名,比如上面例子的Random类。不过有一个包中的类是例外:java.lang。

为了避免一直使用较长的类名字,我们通常会使用import方法: (import [& import-list]),这其中的import-list主要包括两个部分,前一部分表示包所在的路径,后一部分表示需要导入的类名字,比如:

(import '(java.util Random Locale) '(java.text MessageFormat))

这基本上就是Clojure调用Java的全部了。简单吧!

语法糖衣

大部分的java相关操作的语句都有一个简化版,比如new:

(new Random) == (Random .)

对于方位类中的静态元素,直接可以使用/,而不是.

(. Math Pi) == Math/PI

静态方法也可以用/来直接调用

(. System currentTimeMills) == (System/currentTimeMills)

我们甚至可以用更简单的方法来表述对象的函数调用或者域访问:

(. rnd nextInt) = (.nextInt rnd)

为了简化java中多个类不断调用,而如果写成这种前缀形式会显得比较丑陋。Clojure提供了 .. 宏

(.getLocation (.getCodeSource (.getProtectionDomain (.getClass '(1 2)))))

==

(.. '(1 2) getClass getProtectionDomain getCodeSource getLocation)

Java Collection

Clojure提供的Collection支持并发和事物内存机制,性能各方面也优于Java本身的Collection类,所以我们推荐使用Clojure的Collection。然而有一点就是Java的array无法用Clojure的collection来模拟,因此其提供了make-array来创建Java的array。

(make-array class length)
(make-array class dim & more-dims)

上面两种语句都会返回一个java array对象,我们可以使用Clojure提供的seq方法来包裹java array,生成一个clojure的sequence对象。

可以使用下面的方法来获取clojure提供的array方法:

(find-doc "-array")

clojure也提供了一系列的低级操作来支持java array:

(aset java-array index value)
(aset java-array index-dim1 index-dim2 ... value)
(aget java-array index)
(aget java-array index-dim1 index-dim2 ...)
(alength java-array)

通常可以使用clojure的to-array方法直接将一个collection转换为java array:

(to-array ["Easier" "array" "creation"])
#<Object[] [Ljava.lang.Object;@1639f9e3>

into-array提供to-array类似的功能,但是却可以制定array中每一个元素的类型,而不是Object

(into-array String ["Easier", "array", "creation"])

如果没有类名,into-array也会根据参数才猜测之。

Cloure提供了amap函数来循环处理java array中的每一个元素

(amap a idx ret expr)

其表示,首先对传进的array - a 进行复制,并且把复制品绑定为ret,之后将对 a中的每一个元素执行expr,idx绑定的为当前元素的index。最后返回结果array。比如测试例子:

(def strings (into-array ["some" "strings" "here"]))
#'user/strings

(seq (amap strings idx _ (.toUpperCase (aget strings idx))))
("SOME" "STRINGS" "HERE")

areduce是非常类似于amap的高阶函数,它的功能稍微复杂点:

(areduce a idx ret init expr)

Convenience Functions

Clojure作为一个函数式语言,是以函数为参数和返回值的,而Java并不具备该功能。因此Clojure提供了memfn宏用来将Java中的方法转化为Clojure中的方法,以便于作为参数或者返回值继续传递下去。

(map .toUpperCase ["a" "short" "message"])
java.lang.Exception:\ Unable to resolve symbol: .toUpperCase in this context

(map (memfn toUpperCase) ["a" "short" "message"])
("A" "SHORT" "MESSAGE")

更好的办法是利用匿名函数来包裹java函数:

(map #(.toUpperCase %) ["a" "short" "message"])
("A" "SHORT" "MESSAGE")

Clojure还提供了一个检查给定对象是否属于某种类的判断语句instance?

(instance? String 10)
false

Clojure为字符串提供了format函数来代替java本身的format函数

(format "%s ran %d miles today" "Stu" 8)
"Stu ran 8 miles today"

posted @ 2011-10-18 23:24  MMJX  阅读(1763)  评论(0编辑  收藏  举报