2011-12-16 34 views
2

我知道我可以做Common Lisp中的以下內容:SETF Clojure中

CL-USER> (let ((my-list nil)) 
     (dotimes (i 5) 
     (setf my-list (cons i my-list))) 
     my-list) 
(4 3 2 1 0) 

如何Clojure中做到這一點?特別是,如何在不使用Clojure中的setf的情況下做到這一點?

+3

在常見的Lisp中,這將會變得更簡短(作爲我的循環從4到0收集我)。 –

回答

3

爲了線程安全,Clojure禁止局部變量的變異,但即使沒有變異,仍然可以編寫循環。在你想my-list有不同的價值,不過這可以通過遞歸來實現,以及每次循環運行:

(let [step (fn [i my-list] 
      (if (< i 5) 
       my-list 
       (recur (inc i) (cons i my-list))))] 
    (step 0 nil)) 

Clojure中也有辦法「只是做了循環」而不進行新功能,即loop。它看起來像是一個let,但您也可以跳到其正文的開頭,更新綁定,然後使用recur再次運行主體。

(loop [i 0 
     my-list nil] 
    (if (< i 5) 
    my-list 
    (recur (inc i) (cons i my-list)))) 
用遞歸尾調用

「更新」參數可以看起來非常相似變異的變量,但有一個重要的區別:當你在你的Clojure代碼鍵入my-list,其意義將永遠永遠my-list。如果嵌套函數關閉my-list並且循環繼續下一次迭代,嵌套函數將始終看到my-list在嵌套函數創建時的值。一個局部變量總是可以用它的值來代替,並且在進行遞歸調用之後你所擁有的變量在某種意義上是一個不同的變量。

(Clojure的編譯器執行,從而需要對這個「新變量」沒有多餘的空間優化:當一個變量需要記住它的值複製,並當recur被稱爲舊變量被重複使用。)

+0

你以i = 0開頭,每一步都會將它解決,當我<5時停止 - 這意味着立即:)可能你的意思是inc和i> 5. – skaurus

+0

@skaurus:是的,你絕對是對的!固定。 – raek

+1

你可能還是要修復< to > ......對不起) – skaurus

1
 
user=> (range 5) 
(0 1 2 3 4) 
user=> (take 5 (iterate inc 0)) 
(0 1 2 3 4) 
user=> (for [x [-1 0 1 2 3]] 
     (inc x)) ; just to make it clear what's going on 
(0 1 2 3 4) 

setf是狀態突變。 Clojure對此有着非常明確的意見,併爲它提供了工具如果你需要的話。你沒有在上面的情況。

7

Augh在Clojure中做到這一點的方式是不做:Clojure討厭可變狀態(它是可用的,但不鼓勵使用它爲每一件小事情)。相反,請注意這個模式:你真的在計算(cons 4 (cons 3 (cons 2 (cons 1 (cons 0 nil)))))。這看起來非常像一個減少(或者如果你願意,可以摺疊)。所以,(reduce (fn [acc x] (cons x acc)) nil (range 5)),這會產生你正在尋找的答案。

8

我個人的,你Common Lisp中在做什麼翻譯將Clojurewise是:

(into (list) (range 5)) 

導致:

(4 3 2 1 0) 

一點解釋:

功能into星相所有元素到一個集合,這裏是一個新列表,由(list)創建,來自其他一些集合,這裏的範圍是0 .. 4。每個數據結構的conj的行爲不同。對於一個列表,conj表現爲cons:它將一個元素放在列表的頭部並將其作爲新列表返回。那麼這是幹什麼的:

(cons 4 (cons 3 (cons 2 (cons 1 (cons 0 (list)))))) 

這與您在Common Lisp中所做的相似。 Clojure的不同之處在於我們總是返回新的列表,而不是修改一個列表。只有在Clojure真正需要時纔會使用突變。

當然,你也可以得到這個名單馬上,但這可能不是你想知道:

(range 4 -1 -1) 

(reverse (range 5)) 

或...最短的版本,我可以拿出:

'(4 3 2 1 0) 

;-)。

0
(let [my-list (atom())] 
    (dotimes [i 5] 
    (reset! my-list (cons i @my-list))) 
    @my-list) 

(def ^:dynamic my-list nil);need ^:dynamic in clojure 1.3 
(binding [my-list()] 
    (dotimes [i 5] 
    (set! my-list (cons i my-list))) 
    my-list) 
+0

對clojure不太好 – BLUEPIXY

+0

clojure中的數據結構希望是不可變的,除了特殊情況。 – BLUEPIXY

2

爲此,我會用range與手動設置的步驟:

(range 4 (dec 0) -1) ; => (4 3 2 1 0) 

dec降低了與1月底一步,使我們得到0值了。