什么是Clojure
Clojure是一種動態的、強類型的、寄居在JVM上的語言。
Clojure的特性:
- 函數式編程基礎,包括一套性能可以和典型可變數據結構媲美的持久性數據結構
- 由JVM提供的成熟的、高效的運行時環境:所以Clojure可以使用Java類庫,反之Clojure庫也可以被Java使用
- 跟JVM/Java的互操作能力使得很多架構、運維方面的需求可以得到滿足:Clojure代碼可以像Java代碼一樣被打包,然后部署到任何Java應用可以部署的地方
- 一套提供並發、並行語義的機制:Clojure的應用類型強制我們把對象的狀態和對象的標識區分開(這是個天才的思想,參見[1][2]),對於多線程的支持使得我們不用手動加鎖,解鎖也能編寫多線程代碼
- 是一種Lisp方言,因此提供了非常靈活、強大的元編程能力:Clojure保留了Lisp的最好的特性,去掉了Lisp方言的缺陷。
安裝Clojure
因為Clojure需要運行在JVM上所以需要JRE。然后可以在http://clojure.org/community/downloads下載Clojure的代碼。有了這些就可以運行Clojure的REPL了。
運行REPL
從命令行運行運行REPL的方式:1. 進入下載的Clojure目錄;2.運行java -cp clojure-1.8.0.jar clojure.main,如果會看到如下,則成功:
為了以后運行方便可以創建一個shell腳本cljREPL.sh,內容如下:
#!/bin/sh cd /home/namenode/Code/workspace/clojure-1.8.0 java -cp clojure-1.8.0.jar clojure.main
然后,修改執行權限:
chmod +x cljREPL.sh
然后創建軟鏈接:
sudo ln -s /home/namenode/Code/workspace/clojure-1.8.0/cljREPL.sh /bin/cljREPL
這樣在終端直接輸入cljREPL就可以直接運行Clojure的REPL了
安裝Clojure命令工具
在Ubuntu下可以直接用apt-get安裝Clojure。
sudo apt-get install clojure1.6
測試,創建文件balance.clj(例子來自《Java虛擬機並發編程》)
(def balance(ref 0)) (println "Balance is " @balance) (dosync (ref-set balance 100)) (println "Balance is now " @balance)
運行:clojure balance.clj則會打印下圖的結果
語法
Clojure、Java、Python和Ruby中的函數調用語法比較
同像性
Clojure是由Clojure自身的數據結果:原子值(字符串、數字等)和集合的字面量來表示。這種特征就叫“同像性”,或者稱為“代碼即數據”。
Clojure沒有定義一種將會別轉換成AST(Abstract Sytax Tree,抽象語法樹)的語法,Clojure代碼是直接用表示抽象語法樹的Clojure數據結構來寫的。
Clojure使用數據來表示語言代碼的特征使得Clojure代碼可以很容易地用來編寫和轉換其他Clojure代碼。這是宏(Macro)的基礎,Clojure中的元程序編程工具要比C語言中提供的那種宏以及其他文本預處理器要強勁的多。
Clojure Reader
Clojure reader的功能是把程序員寫的文本格式的代碼轉換成Clojure數據結構。Reader的所有操作是由一個叫read的函數定義的,這個函數從一個字符流里讀入代碼的文本形式,產生這個文本所對應的數據結構。Clojure的REPL就是使用Reader來讀入文本代碼的, reader的作用其實可以看做是反序列化的過程。與read和read-string對應的兩個函數是pr和pr-str,這兩個函數是序列化的過程。
所有Clojure的數據結構和值序列化之前都是既對人可讀,又對機器可讀
標量字面量
字符串
和Java等語言一樣 “Hello World”
而且Clojure天然支持多行
布爾值
Clojure中用true和false表示布爾值
nil
Clojure中的nil和Java中的null是類似的,在判斷中nil是邏輯的false
字符
字符字面量是通過反斜杠加字符表示的:
對於Unicode編碼和octal編碼,可以使用對用前綴:
同時對於一些特殊字符也有對應的常量:
\space \newline \formfeed \return \backspace \tab
關鍵字
關鍵字始終是以冒號開頭,它可以包含任意非空字符。如果關鍵字里面包含/,表示這個關鍵字是命名空間限定的。如果關鍵字是以兩個冒號(::)開頭的,那么表示是當前命名空間的關鍵字。如果關鍵字以兩個冒號開頭,同時又包含了/,如::alias/kw,那么表示某個特定命名空間里面的關鍵字。這個設計與XML里面的命名空間實體的用法和動機是一樣的,也就是為了讓同一個名字在不同的命名空間里有不同的值和語義。
符號
符號也是一種標識符,符號的值是它所代表的Clojure運行時里面的那個值,這個值可以使var所持有的值、Java類、本地引用等。
數字
十六進制:0xff 八進制:040(以0開頭) 任意進制:BrN(N表示數字,B表示進制) 有理數:用比例數表示
正則表達式
以#開頭,不需要對反斜杠轉義
注釋
- 單行注釋以分號開頭
- 形式級別的注釋#_宏,告訴reader忽略下一個Clojure形式
空格和逗號
在Reader眼里,逗號就是空格
集合字面量
命名空間
所有的Clojure代碼都是在一個命名空間中被定義和求值的。命名空間可以禪城Ruby和Python的module,Java的package。
Clojure中的一種引用類型var是一種可修改的內存地址,從而可以保存任何值,在var被定義的命名空間里,var和一個符號相關聯,然后我們就可以通過這個符號來使用這個var,從而得到這個var的值。
在Clojure中var是用def來定義的。如:
在當前命名空間user中定義了一個名叫x的var
當前的命名空間始終綁定到*ns*上
符號求值
阻止求值:quote
阻止求值也可以用單引號表示
代碼塊:do
do會依次傳入進來所有的表達式,並且把最后一個表達式的結果作為返回值
定義Var:def
本地綁定:let
所有本地綁定都是不可變的;let的綁定數組在編譯期間在編譯器間可以對通用集合進行解構,利用解構,可以大大簡化從綁定數據中抽取想要的數據的操作。
解構:let
很多Clojure函數都接受順序性數據結構和map作為參數或返回值,而且接受或返回抽象數據類型。這使函數在調用Clojure類庫時,不需要額外的代碼去對接具體數據結構的實現,也就不需要一些glue code來做類型轉換之類的事情,可保持代碼簡單
定義函數:fn
函數是把參數值綁定到參數上,在執行
defn
defn基於fn,封裝了def和fn的功能,定義一個具有名的函數
判斷:if,when,cond
循環:loop和recur
與Java的互操作:.和new
異常處理:try和throw
狀態修改:set!
鎖的原語:monitor-enter和monitor-exit
參考
- http://ifeve.com/stm-1/
- Dr. Alan Kay, 《on the Meaning of “Object-Oriented Programming”》, http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en
- Chas Emerick, Brian Carper, Christophe Crand,《Clojure編程》