2012-04-28 50 views
1

如果我理解正確的Clojure可以返回列表(如在其他的Lisp),而且載體和集。什麼決定了何時創建集合?

我真的不明白就是爲什麼有沒有總是返回的集合。

例如,如果我採取以下的代碼:

(loop [x 128] 
    (when (> x 1) 
    (println x) 
    (recur (/ x 2)))) 

它確實打印128 64 32 16 8 4 2但是,這只是因爲的println被調用並的println具有副作用( ?)印刷東西。

所以我試圖用這個替換它(去除的println):

(loop [x 128] 
    (when (> x 1) 
    x 
    (recur (/ x 2)))) 

而且我期待得到一些收集(據說列表),像這樣:

(128 64 32 16 8 4 2) 

而是我得到

我不明白,這決定了創建一個收集,什麼不和你如何從一個到另一個切換。另外,看到Clojure以某種方式鼓勵一種「功能性」編程方式,是不是應該幾乎總是返回集合?

爲什麼,顯然不返回任何集合如此多的功能?什麼是做這些返回集合的慣用方法?

例如,我將如何通過首先構建集合解決上述問題,然後迭代(?)的其他慣用的方式將得到的列表/矢量?

首先,我不知道如何來改造循環以使它產生別的東西比然後我試過如下:

(reduce println '(1 2 3)) 

但它打印「1個2nil 3nil」而不是「1 2 3nil」我期待着。

我意識到這是基本的東西,但我剛開始和我顯然在這裏失去了基本的東西。

(PS:重新標記適當,我不知道我應該在這裏使用的術語)

+2

你的'when'是不對的,你應該使用'if'理解。即使如此,你會返回一個數字「x」。你實際上必須從某個地方創建一個列表來返回列表。 – 2012-04-28 16:24:17

+0

@Seth Carnegie:好的但是......我的問題是如何確定什麼是創造什麼:例如,爲什麼*當*不在這裏工作,我怎麼知道?這是否意味着*當*只能用在有副作用的函數中? – 2012-04-28 16:35:54

+1

@CedricMartin函數式編程沒有什麼魔力。返回一個數字就是這樣 - 返回一個數字,沒有別的。 (在你的情況中,由於數字被返回到一個不關心返回值的上下文中(非'尾部'位置的'when'主體子句),它會立即被丟棄,所以什麼都不會發生。)如果你想要一個集合,你需要明確地創建它。關於'when':'when'的用途恰恰是*副作用,所以在學習函數式編程時,最好避免它。 – 2012-04-28 17:29:43

回答

3

其他一些評論人士指出,如果沒有真正的工作一樣,如果 - 但我不認爲這確實是你的問題。

循環和循環表單創建迭代 - 就像其他語言的循環一樣。在這種情況下,當您打印時,確實只是爲了副作用。如果你想返回一個序列,那麼你就需要建立一個:

(loop [x 128                           
     acc []]                          
    (if (< x 1)                          
    acc                            
    (recur (/ x 2)                         
      (cons x acc))))                       

=> (1 2 4 8 16 32 64 128) 

在這種情況下,我換成你在哪裏用易復發,增加了X的形式調用的printf現貨那個蓄電池的前面。在x小於1的情況下,代碼返回累加器 - 從而產生一個序列。如果你想添加到載體,而不是前面的結尾,將其更改爲連詞:

(loop [x 128                          
     acc []]                         
    (if (< x 1)                         
    acc                           
    (recur (/ x 2)                        
      (conj acc x))))                      

=> [128 64 32 16 8 4 2 1] 

你都拿到爲零,因爲那是你的表達式的結果 - 最終的println返回了什麼。

這一切是否有意義?

reduce是不完全相同的事情 - 它用於通過重複將二進制函數(帶有2個參數的函數)應用於序列的初始值和第一個元素或第一個元素第一次迭代的序列的兩個元素,然後後續迭代傳遞前一次迭代的結果和序列中的下一個值。一些例子可能會有幫助:

(reduce + [1 2 3 4])                        

10 

這會執行以下操作:

(+ 1 2) => 3 
(+ 3 3) => 6 
(+ 6 4) => 10 

減少將導致無論最終的結果是從二元函數執行 - 在這種情況下,我們減少的數字在序列中轉化爲所有元素的總和。

您也可以提供一個初始值:

(reduce + 5 [1 2 3 4])                        

15 

它執行以下操作:

(+ 5 1) => 6 
(+ 6 2) => 8 
(+ 8 3) => 11 
(+ 11 4) => 15 

HTH,

凱爾

+0

+1,這是很有道理的。這是* println *的副作用使我迷失方向:我不明白爲什麼*無*是最後拋出的。但現在它更清晰了。當然,可能更多的問題太...:) – 2012-04-28 21:41:31

2

在收集廣義抽象被稱爲在Clojure中的序列和許多數據結構實現了這種抽象您可以在這些數據結構上使用所有與序列相關的操作,而不必考慮傳遞給您的函數的數據結構。

就樣本代碼而言 - 循環,遞歸用於遞歸 - 所以基本上任何想要使用遞歸解決的問題都可以使用它來解決,典型例子是階乘。儘管可以使用循環創建向量/列表 - 通過使用累加器作爲向量並將其附加到它並在遞歸的存在條件下返回累加向量 - 但您可以使用reductionstake-while函數執行操作,如下所示。這將返回一個懶惰的序列。

例:

(take-while #(> % 1) (reductions (fn [s _] (/ s 2)) 128 (range))) 
+0

或者:'(取,而#(> 1%)(迭代#(/%2)128))' – mikera 2012-05-09 10:11:34