我知道我可以做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的情況下做到這一點?
我知道我可以做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的情況下做到這一點?
爲了線程安全,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
被稱爲舊變量被重複使用。)
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對此有着非常明確的意見,併爲它提供了工具如果你需要的話。你沒有在上面的情況。
Augh在Clojure中做到這一點的方式是不做:Clojure討厭可變狀態(它是可用的,但不鼓勵使用它爲每一件小事情)。相反,請注意這個模式:你真的在計算(cons 4 (cons 3 (cons 2 (cons 1 (cons 0 nil)))))
。這看起來非常像一個減少(或者如果你願意,可以摺疊)。所以,(reduce (fn [acc x] (cons x acc)) nil (range 5))
,這會產生你正在尋找的答案。
我個人的,你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)
;-)。
爲此,我會用range
與手動設置的步驟:
(range 4 (dec 0) -1) ; => (4 3 2 1 0)
dec
降低了與1月底一步,使我們得到0值了。
這就是我一直在尋找的模式:
(loop [result [] x 5]
(if (zero? x)
result
(recur (conj result x) (dec x))))
我找到了答案Programming Clojure (Second Edition) by Stuart Halloway and Aaron Bedra。
在常見的Lisp中,這將會變得更簡短(作爲我的循環從4到0收集我)。 –