Clojure 程式是由許多運算式 (Expression) 組合而成。在 Clojure 中,Expression (運算式) 也被稱爲 Form (形式)。一個運算式執行之後 (或叫做 Evaluate 求值),得到執行後的結果。
以 Hello World 範例程式爲例,以下是 Clojure 版本:
(str "Hello " "World")
;; => "Hello World"
以上的運算式使用列表 (List) 來表示函式呼叫,列表使用左右兩個小括號來表示。括號中分別有三個元素:符號 (Symbol) str
,以及兩個字串:Hello
與 World
。
符號 (Symbol) str
,對應到 Clojure 內建的函式。Clojure 會找到這個符號對應的函式並呼叫它,執行的結果是將帶入的字串串接起來。
Clojure 與 LISP 家族跟其他語言不同的是,語法採用前置表示法 (或稱做波蘭表示法),將函式或運算元擺放在括號內的第一個位置,之後的位置則擺放各個參數。有些情況下第一個位置擺放的並非函式或運算元,這種特殊的運算式被稱爲 Special forms。
在其他非使用前置表達式的語法中,要將一連串數字相加起來會寫成:
1 + 2 + 3 + 4 + 5
而使用前置表達式的 Clojure 只要寫成:
(+ 1 2 3 4 5)
或是在其他語言中的 +
與 *
的執行優先順序需要注意,一搞錯結果就會不一樣:
1 + 2 * 3
使用前置表達式就非常清楚,誰先處理後處理則一目瞭然:
(+ 1 (* 2 3))
Clojure 提供跟其他主流語言類似的資料型態與資料結構,而有些則與主流程式語言不盡相同,這裡先提供大致的導覽,之後文章將會詳細介紹。
Clojure 提供了跟主流語言類似數字表示法,常用的有整數、浮點數和有理數。
整數:
42
;; => 42
浮點數:
3.14
;; => 3.14
有理數:
(/ 1 3)
;; => 1/3
Clojure 的字串使用雙引號方式來表示,引號內擺放需要的文字。型態是 Java 中的字串類型 java.lang.String:
"Issac Asimov"
;; => "Issac Asimov"
Clojure 表現字符 (Character) 的方式與主流程式語言稍有不同,它在欲使用的字符之前加上反斜線 (\):
\A
;; => \A
\B
;; => \B
\b
;; => \b
\a
;; => \a
除了可視字符之外,以下是其它特殊字符的使用方式:
\space
;; => \space
\newline
;; => \newline
\formfeed
;; => \formfeed
\return
;; => \return
\backspace
;; => \backspace
\tab
;; => \tab
Clojure 程式語言使用 true
和 false
來表示邏輯上的真與假:
true
;; => true
false
;; => false
除了 true
以及 false
之外,Clojure 還加入了 nil
表示不存在與虛無。當用在邏輯判斷時,nil
跟 false
被當作邏輯上的假。
符號用來指稱某種東西,例如前面提到的 str
和 +
用來表示函式與運算元。Clojure 類似於其他程式語言用來定義變數的方式,就是使用 def
定義一個符號以及它對應的事物。
(def clojurist "Bob")
;; => #'user/clojurist
clojurist
;; => “Bob”
使用 def
會建立符號 clojurist
連結到 "Bob"
字串。在 REPL 中看到結果 clojurist
加了 user 與斜線 (/),斜線前面的符號指的是 clojurist
的命名空間 (namespace)。在 REPL 中,預設的命名空間是 user
。
關鍵字 (Keyword) 與符號的功能類似,也是標識符號 (Identifier),但是關鍵字必須以冒號 (:) 爲開頭,而且關鍵字不代表其他資料,只代表它自己。通常跟映射 (Map) 搭配使用,作爲映射的索引鍵 (Key)。
:foo
;; => :foo
:bar
;; => :bar
{:Lisp "McCarthy" :Clojure "Hickey"}
;; => {:Lisp "McCarthy", :Clojure "Hickey"}
Clojure 將前面加上井號 (#) 的字串視爲正則表達式,型態爲 Java 中的 java.util.regex.Pattern:
(class #"[0-9A-Za-z]")
;; => java.util.regex.Pattern
正則表達式與內建的函式,如 re-seq
、re-find
與 re-match
一起搭配使用。
當資料變多變雜時,會需要程式語言提供容器將相似的資料整理在一起。Clojure 提供四種群集型態 (Collections):列表 (List)、向量 (Vector)、映射 (Map) 與集合 (Set)。
;; List
'(1 2 3)
;; => (1 2 3)
;; Vector
[1 2 3]
;; => [1 2 3]
;; Map
{:author "Isaac Asimov" :title "I, Robot"}
;; => {:author "Isaac Asimov", :title "I, Robot"}
;; Set
#{1 2 3 4}
;; => #{1 4 2 3}
Clojure 使用空白分隔運算式中的各個元素,主流程式語言中則是使用逗號 (,)。其實也可以使用逗號,它的功能跟空白完全一樣,但是依照編寫的習慣,建議使用空白來區隔元素。逗號通常用來分隔映射裡的元素,以增加可讀性。
{:name "Catherine", :age 40}
以分號 (;) 開頭的文字被視爲單行註解,Clojure 會將它忽略不執行。如果想要撰寫多行註解,可以使用 comment
。
(+ 1 2) ; the result is 3
;; => 3
(comment
I have a dream that one day this nation will rise
up, live out the truth meaning of its creed.)
;; => nil
Clojure 是一個函數式語言,函式的定義與使用至關重要。通常使用 defn
來定義函式:
(defn hello [name]
(str "Hello, " name))
;; => #’user/hello
defn
的第一個參數用來表示函式的名稱,名稱之後則是新定義函數的參數,中括號裡代表各參數的名字,之後便是函式的本體 (Body)。當函式被呼叫時,本體中的表達式將會被求值,所得到的值就是此函式的返回值。
從本篇文章中你已經知道了 Clojure 可以表達的數字類型,還有字串與字符的表達方式;也了解用來判斷邏輯真假的布林型態、指稱事物的符號和關鍵字型態;還知道了四種集合型態:列表、向量、映射與集合。還知道了定義函式的方法。
還不賴吧?今天就先到這裡,下一篇文章再見囉!