計(jì)算(Evaluation)
user=> "hi"
;; "hi"
user=> :foo
;; :foo
user=> [1 2 3]
;; [1 2 3]
只要有表達(dá)式,clojure都會(huì)嘗試去計(jì)算,并且重復(fù)打印在控制臺(tái)。如果是括號(hào)開(kāi)頭"(",clojure會(huì)把它當(dāng)成宏,特殊形式或者函數(shù)。
函數(shù)訪問(wèn)(Function Calls)`
(my-func arg1 arg2 arg3)
在括號(hào)"("右邊的符號(hào)就是函數(shù)的名稱(my-func就是函數(shù)名稱),clojure先計(jì)算出所有參數(shù)的值,再把這些參數(shù)值賦給函數(shù)。
有點(diǎn)繞,我的理解是像下面這種嵌套調(diào)用,先從最里層開(kāi)始計(jì)算,依次計(jì)算"+"->"foo-bar"->"other-func"->"my-func2"->"my-func"
(my-func (my-func2 arg1
arg2)
(other-func arg-a
(foo-bar arg-x
arg-y
(+ arg-xx
arg-yy
arg-zz))
arg-b))
主要注意的是,在clojure里面沒(méi)有"操作符",像"+,-,*,>,=,not="都是函數(shù)名稱。
宏與特殊形式(Macros and Special Form)
- 上面講過(guò),如果一個(gè)表達(dá)式以括號(hào)"("開(kāi)頭,clojure會(huì)先去檢查是宏還是特殊形式,這些形式都區(qū)別于普通的計(jì)算,會(huì)被編譯器特殊對(duì)待。
- 這里關(guān)于宏的描述有點(diǎn)拗口,我的理解是:使用普通的clojure代碼編寫(xiě),但是返回轉(zhuǎn)換/擴(kuò)展的代碼在某些場(chǎng)合使用,即:可以通過(guò)宏產(chǎn)生出新的語(yǔ)法格式。先不管了,看了后面代碼應(yīng)該就明白了。
- 使用defmacro來(lái)定義宏'
- 宏在編譯的時(shí)候被調(diào)用,并且在其他代碼編譯之前。就像if,def,let一樣,這些都是硬編碼到編譯器,但效果是樣的。
引號(hào)(Quoting)
'(+ 1 2 3)
;; ? (+ 1 2 3)
如果是括號(hào)開(kāi)頭就會(huì)被當(dāng)做函數(shù),這種方式后面會(huì)介紹
Let and Locals
如果你想使用詞法作用域,可以使用let表達(dá)式
(let [width 10
height 20
thickness 2]
(println "hello from inside the `let`.")
(* width
height
thickness))
;; ? 400
再來(lái)一個(gè)列子,let會(huì)得到最后一個(gè)表達(dá)式的值。所以之前可以放一些其他的東西,例如println等。
(let [x 2
x (* x x)
x (+ x 1)]
x)
;; ? 5
需要注意:println函數(shù)本身計(jì)算得到的值都是nil,我們不會(huì)用它來(lái)做任何計(jì)算,這種叫做side-effect,后面會(huì)簡(jiǎn)單介紹。
Namespaces
Clojure使用命名空間進(jìn)行分組,避免函數(shù)名稱沖突。所有的函數(shù)都有自己的命名空間,所有的core函數(shù)都在clojure.core這個(gè)命名空間下:
(clojure.core/println "hi")
- 這是println的fully-qualified(全名),這種格式是"namespace/symbol",舉個(gè)例子,如果有個(gè)函數(shù)myfun在src/foo_bar/core.clj,那么可以使用foo_bar.core/myfun使用。
- 一般來(lái)說(shuō)一個(gè)源文件對(duì)應(yīng)一個(gè)namespace,通常包含一個(gè)庫(kù)。在源文件的頂部,一般是ns xxx來(lái)聲明namespace。
使用別名來(lái)引用庫(kù),這樣就可以使用str/(為什么有斜杠還不清楚,后面應(yīng)該會(huì)使用到)來(lái)代替clojure.string
(require '[clojure.string :as str])