Clojure語法學習-循環

do和塊語句

在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

  1. 首先,咱們須要提供一個在每次循環中都會被執行的語句——循環體
  2. 若是不是無限循環,咱們須要提供退出條件,當這個條件知足時,再也不循環。

在Clojure中orm

  1. 首先,咱們須要提供一個在每次循環中都會被執行的語句——循環體
  2. 咱們須要提供循環條件。它這個條件知足時,繼續下一次循環。

也就是說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]

這種代碼怎麼看着這麼眼熟呢?這不就像是尾遞歸嗎?

相關文章
相關標籤/搜索