Clojure 學(xué)習(xí)筆記 :1 初探 Clojure

Clojure 零基礎(chǔ) 學(xué)習(xí)筆記


歡迎來到 Clojure 的世界宰睡。

讓我們先從最經(jīng)典的 hello world 開始吧唾戚。
我們使用鍵盤在 REPL 的輸入框里輸入 (print "hello world!"),回車咳秉!
屏幕中就會顯示:

=> (print "hello world!")
hello world!
nil

(如果你還不知道怎么啟動 REPL爽蝴,你可以看一下這篇文章:“最小化”運行 Clojure REPL

首先說明本文的幾個約定

  • => 后面跟隨的內(nèi)容,表示在 REPL[1] 里輸入的內(nèi)容
  • => 之后另起新行出現(xiàn)的代碼昧辽,為 REPL 返回 (即 REPL 中的 Print) 的內(nèi)容
  • ;= 之后出現(xiàn)的內(nèi)容,表示上一個表達(dá)式的值

如你所見登颓,Clojure 所擁有的 REPL 環(huán)境可以快速地與你進(jìn)行交互 --- 表達(dá)式[2]被立即執(zhí)行搅荞。

現(xiàn)在讓我們看看這三行代碼分別表示什么吧

  • 第 1 行是我們在 REPL 中輸入的內(nèi)容,比如使用鍵盤輸入框咙。這段代碼的含義是咕痛,告訴 REPL ,我們要執(zhí)行 print 函數(shù)喇嘱,以及提供函數(shù)所需的參數(shù)
  • 第 2 行的 hello world!print 函數(shù)的副作用[3]茉贡。(而我們所需要的正是這個副作用)
  • 第 3 行的 nilprint 函數(shù)的返回值[4]print 函數(shù)始終返回 nil者铜。( nil 在 Clojure 中表示空值)

所以我們可以大概知道 REPL 是怎么運行的:
首先腔丧,他接受你的輸入。
然后王暗,執(zhí)行你所輸入的代碼悔据,如果有副作用就會觸發(fā)副作用庄敛。
最后俗壹,它返回你所輸入的代碼的值。REPL 總是把你所輸入的表達(dá)式的值在最后一行顯示出來

函數(shù)藻烤,是 Clojure 里最為重要也是最為基本的組成部分绷雏。
就如同你在中學(xué)數(shù)學(xué)學(xué)習(xí)到的 f(x,y) 一樣怖亭,函數(shù)一般由三部分組成:

  1. 函數(shù)的名稱涎显。
    print。在 Clojure 中兴猩,它寫在在小括號 () 中的第一個位置期吓。
  2. 函數(shù)的參數(shù)。
    如果函數(shù)可以接受多個參數(shù)倾芝,多個參數(shù)之間用空格隔開讨勤。(也可以用 ,
  3. 函數(shù)的返回值。
    也就是函數(shù)的值晨另。

所以 f(x潭千,y) 在 Clojure里就表示為 (f x y)

此例中的 print 函數(shù)
它接收任意數(shù)量的參數(shù),
它的返回值永遠(yuǎn)是 nil借尿,也就是空刨晴,空值屉来。

print 函數(shù)除了返回值之外,還擁有一個“副作用”狈癞,那就是它會依次把每個參數(shù)的顯示在屏幕上 茄靠。(準(zhǔn)確來說是 *out* 輸出流)

函數(shù)像是一個黑盒子,你往里扔參數(shù)蝶桶,他向你扔出返回值嘹黔。
假如除此之外,這個黑盒子還打了你一巴掌莫瞬,那這一巴掌就是函數(shù)的“副作用”儡蔓。
如果你是為了得到你的返回值,那這個函數(shù)的“功能”就是返回的這個值疼邀。如果你想要享受痛苦喂江,那這一巴掌就是他的“功能”。

這里我們顯然利用的是 print 函數(shù)的副作用旁振,對我們來說它才有用获询。
print 函數(shù)的返回值永遠(yuǎn)為 nil,所以也就不那么重要了拐袜。


Clojure 試圖求值一切
函數(shù)的值等于它的返回值吉嚣,而字符串的值就簡單的等于他看起來的樣子。
(雙引號 "" 中的內(nèi)容稱之為字符串蹬铺,它可以用來存儲簡單的文字或者數(shù)據(jù)尝哆,是程序設(shè)計語言中非常常見的 “明星” 。)

你可能對上面這一大堆話并不是很理解
沒關(guān)系甜攀,我們多看例子

比如我們可以給 print 函數(shù)更多的參數(shù)

=> (print "hello world!" "hello again!" "bye!")
hello world! hello again! bye!
nil

或者一個參數(shù)也不給它

=> (print)
nil

觀察結(jié)果
我們看到 print 函數(shù)果然顯示了它的副作用 --- 依次顯示每個參數(shù)的值秋泄。
例外地,如果沒有參數(shù)规阀,它自然也就沒有副作用可以被觸發(fā)恒序。
最后,它的返回值 nil 總是在最后一行被顯示谁撼。


Clojure 的“括號表示法”是可以嵌套的

=> (print (print "I love Rock!!!"))
I love Rock!!!nil
nil

為什么會出現(xiàn)這種結(jié)果呢歧胁?
重復(fù)一遍,Clojure 試圖求值一切內(nèi)容
函數(shù)的值是它的返回值厉碟,字符串的值是它本身…
這個例子的執(zhí)行步驟是這樣的

  1. 從左往右喊巍,找到第一個括號要執(zhí)行的函數(shù)為 print
  2. print 函數(shù)的副作用是打印每個參數(shù)的值
  3. 但是這個參數(shù)的值無法直接確定,因為它并不是一個可以被直接求值的東西 --- 它又是一個函數(shù)墨榄。而函數(shù)也是有值的玄糟,函數(shù)的值就是它的返回值!
  4. 程序轉(zhuǎn)而執(zhí)行內(nèi)層的 (print "I love Rock!!!") 袄秩。字符串的值可以直接被得到阵翎。所以內(nèi)層 print 函數(shù)發(fā)現(xiàn)它所有的參數(shù)都可以直接被求值逢并。于是它就開始發(fā)揮它的副作用了 --- 把每個參數(shù)的值打印出來,I love Rock!!! 就顯示出來了郭卫。
  5. 此時內(nèi)層函數(shù)的值確認(rèn)了 --- **內(nèi)層 print 函數(shù)的值等于它的返回值 nil **(雖然你一眼就能知道返回值永遠(yuǎn)為 nil砍聊,但計算機程序沒有這個本事,它只能執(zhí)行之后才能知道)
  6. 外層函數(shù)發(fā)現(xiàn)內(nèi)層所有的參數(shù)都已經(jīng)求值完畢贰军,
    (如果這個時候時間靜止的話玻蝌,由于內(nèi)層的“謎題”已經(jīng)被解開,那我們的代碼可能就會變成像這個樣子)
 (print nil)

此時外層 print 函數(shù)的副作用發(fā)生词疼!輸出每個參數(shù)的值俯树,即輸出內(nèi)層函數(shù)的值 --- nil

  1. 最后外層函數(shù)返回值 nil 顯示在屏幕上贰盗。

如果你使用一些集成開發(fā)環(huán)境许饿,那么你可以看到 print 函數(shù)的副作用所顯示的 nilprint 函數(shù)的返回值 nil 的顯示效果(如顏色和字體)看起來是不同的

一整句嵌套的表達(dá)式的返回值只有一個!它取決于最外層的那個函數(shù)的返回值舵盈!此例中即為最外層的那個print 的值 nil

同樣陋率,你可能對上面這一大堆話并不是很理解
我們再來幾個例子
這次來介紹一個新的函數(shù) println
它與print 函數(shù)的唯一不同在于,每次產(chǎn)生副作用打印時秽晚,自動在末尾換行

=> (println (println "I love Rock!!!"))
I love Rock!!!
nil
nil
=> (println (print "I love Rock!!!") (println "I love Rock too!!!") (print "I love you..."))
I love Rock!!!I love Rock too!!!
I love you...nil nil nil
nil

可以看到瓦糟,最外層 println 函數(shù)在等待所有參數(shù)的值依次求值完畢后,副作用發(fā)生赴蝇,一次性輸出了三個 nil 菩浙,然后顯示了自己的返回值
函數(shù)返回值是自動換行顯示的(有些 REPL 環(huán)境并不自動換行,取決于具體實現(xiàn))扯再,println 函數(shù)的換行效果指的是在副作用的末尾換行芍耘,即打印完畢后換行,此例中是在 "I love Rock too!!!" 后換了一行


作為一個程序設(shè)計語言熄阻,計算自然是最基礎(chǔ)的。
但與其它語言或者日常習(xí)慣不同的一點倔约,Clojure 的計算表示使用前綴表達(dá)式秃殉,即“運算符”放在“操作數(shù)”之前。
在 Clojure 里浸剩,運算符號同樣是個普通的函數(shù)(甚至不是一個關(guān)鍵字)
而函數(shù)理所當(dāng)然要放在括號的第一個位置

=> (+ 1 1)
2

加法函數(shù) + 接收任意數(shù)量的表達(dá)式作為參數(shù)钾军,它的返回值是各個參數(shù)的和,它沒有副作用绢要。
同樣吏恭,加法函數(shù)是可以嵌套的

=> (+ 3 (+ 1 22))
26

等價于

=> (+ 3 1 22)
26

在上面的嵌套示例中, + 兩個參數(shù)是 3(+ 1 22) 這兩個表達(dá)式重罪。(數(shù)字 3 也是一個正確的表達(dá)式)這兩個表達(dá)式之間最好加上一個空格樱哼,這樣會使得代碼的層次感更好哀九,也遵循了參數(shù)之間使用空格隔開的規(guī)則。
與之前的例子相似搅幅,在遇到有參數(shù)需要進(jìn)一步求值時阅束,會先求內(nèi)層的值
這種做法使得你無需記憶無趣又無用的運算優(yōu)先級
因為每個運算符號一定在括號的第一個位置,所以你總是能一層一層的找到唯一的計算順序

=> (+ 2 (* 8 2));等價于中綴表達(dá)式 2 + 8 * 2
18

=> (* 2 (+ 8 2));等價于中綴表達(dá)式 2 * (8 + 2)
20

現(xiàn)在你已經(jīng)初步了解了 Clojure 的執(zhí)行過程與它的語法
接下來你會逐漸適應(yīng)這種看似奇怪的表達(dá)方式
最終陶醉于這種表達(dá)方式所帶來的優(yōu)雅茄唐、簡潔和便利
以及這種強大的語言所產(chǎn)生的無法抗拒的魅力


  1. REPL 即 Read-Eval-Print Loop --- “讀取-求值-輸出” 循環(huán) ?

  2. 表達(dá)式:你可以簡單理解為一段可以被 Clojure 所執(zhí)行的代碼 ?

  3. 副作用(Side effect):副作用是指息裸,表達(dá)式被求值后,對外部世界的狀態(tài)做的某些改變沪编。當(dāng)我們對一個如 (+ 1 2)
    這樣純粹的 Lisp 表達(dá)式求值時呼盆,沒有產(chǎn)生副作用。它只返回一個值蚁廓。但當(dāng)我們調(diào)用 print 時宿亡,它不僅返回值,還印出了某些東西纳令。這就是一種副作用挽荠。(引用自ANSI Common Lisp 中文翻譯版?

  4. 返回值即為表達(dá)式執(zhí)行后的值,同時是表達(dá)式本身的值平绩,Clojure 中所有的表達(dá)式都有值 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末圈匆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捏雌,更是在濱河造成了極大的恐慌跃赚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件性湿,死亡現(xiàn)場離奇詭異纬傲,居然都是意外死亡,警方通過查閱死者的電腦和手機肤频,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門叹括,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宵荒,你說我怎么就攤上這事汁雷。” “怎么了报咳?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵侠讯,是天一觀的道長。 經(jīng)常有香客問我暑刃,道長厢漩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任岩臣,我火速辦了婚禮溜嗜,結(jié)果婚禮上宵膨,老公的妹妹穿的比我還像新娘。我一直安慰自己粱胜,他們只是感情好柄驻,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著焙压,像睡著了一般鸿脓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涯曲,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天野哭,我揣著相機與錄音,去河邊找鬼幻件。 笑死拨黔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绰沥。 我是一名探鬼主播篱蝇,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼徽曲!你這毒婦竟也來了零截?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤秃臣,失蹤者是張志新(化名)和其女友劉穎涧衙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奥此,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡弧哎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了稚虎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撤嫩。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖祥绞,靈堂內(nèi)的尸體忽然破棺而出非洲,到底是詐尸還是另有隱情,我是刑警寧澤蜕径,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站败京,受9級特大地震影響兜喻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赡麦,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一朴皆、第九天 我趴在偏房一處隱蔽的房頂上張望帕识。 院中可真熱鬧,春花似錦遂铡、人聲如沸肮疗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伪货。三九已至,卻和暖如春钾怔,著一層夾襖步出監(jiān)牢的瞬間碱呼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工宗侦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愚臀,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓矾利,卻偏偏與公主長得像姑裂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子男旗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容