在Scala中,花括號{}括起來的語句構成一個block,它的值就是最後一個語句的值。java
scala> val a = { | println("a") | 1} a a: Int = 1
{println("a"); 1}的值爲1。ruby
在Clojure中,有時須要使多個form組成一個block, 這個block的值是最後一個form的值。這時候就用do函數
user=> (def a (do (println "a") 1)) a #'user/a user=> a 1
do takes any number of forms, evaluates them all, and returns the last.oop
do接受任意多的form做爲參數,對它們分別求值,而後返回最後一個form的值。lua
有哪些要素才能構成一個循環?spa
在Java中scala
在Clojure中orm
也就是說Java須要咱們告訴它何時退出循環,而Clojure須要咱們告訴它什麼時候繼續循環。blog
Java的for循環是這樣的遞歸
for(int i = 0; i < 10; i++) System.out.println(i);
能夠認爲 i< 10, i++以及System.out.println(i)構成了循環體。退出條件爲i<10。
while循環與for循環的不一樣之處在於while沒法聲明只在循環內部使用的變量。在上邊的for循環中, i只在循環內部使用。若是咱們想讓while有相似的功能(固然,while沒這功能),那麼while須要接受一個初始化語法,變成
while(int i = 0)(i < 10){ println(i); i++; }
在Clojure中,一樣能夠以binding的形式提供初始化語句, 以及提供循環體。這經過loop這種form來實現
(loop [bindings *] exprs*)
這就相似前邊這個增強版的while。同時,在while循環中須要break來打破循環; 在Clojure中,咱們須要一種form來繼續循環,這就是recur。能夠認爲Java的循環是主動的,而Clojure中的是被動的,你必須在代碼中驅動它前進。
(loop [a 0] (if (< a 10) (do (println a) (recur (+ 1 a)))))
recur使得程序從新開始執行loop。可是如何程序中是簡單地從新執行loop,它就只是原地踏步,由於全部的綁定都始終是初始值。因此recur不只轉變了程序的執行流,並且修改了loop開始的綁定。即,recur使得loop開始對a的綁定變成了(+ 1 a)。
假如,咱們在loop開始的時候多提供一個綁定
(loop [a 0 b 1] (if (< a 10) (do (println a) (recur (+ 1 a)))))
REPL就會告訴咱們提供給recur的參數個數不對
CompilerException java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 2 args, got: 1
實際上,recur不只能夠用於loop,也能夠用於函數,它使得函數被從新執行。
舉個書上的例子
(defn countdown [result x] (if (zero? x) result (recur (conj result x) (dec x))))
執行(count down [] 5)會輸出返回值[5 4 3 2 1]
這種代碼怎麼看着這麼眼熟呢?這不就像是尾遞歸嗎?