2014-07-17 46 views
1

運行Clojure中下面的代碼給出了一個StackOverflow的錯誤:COND Clojure中與成千上萬的條款

(cond 
(= 1 2) 1 
(= 2 3) 2 
(= 3 4) 3 

... 

(= 1022 1023) 1022 
(= 1023 1024) 1023 
:else 1024) 

我想創建一個函數/宏,可以處理條款的數量巨大,而無需創建一個堆棧溢出。

請告訴我如何嘗試這個。

+1

你能解釋一下你試圖用'cond'解決的問題嗎?它可以幫助我們回答你的問題。 – Kyle

+0

Clojure沒有tail調用優化,但是你可以[使用recur](http://clojure.org/functional_programming#Functional%20Programming--Recursive%20Looping)來避免堆棧問題。 –

+0

感謝您的評論。 @Kyle:當我將一個巨大的SQL case語句翻譯成clojure時遇到了這個問題。實際上,sql語句會被更好地編碼爲連接。所以,說實話我主要是問,因爲認爲這是一個有趣的編碼問題。 – user2179977

回答

5

如果你看完整的堆棧跟蹤,你會發現cond發出一個嵌套深的if結構;當編譯器試圖解析這個結構時,就會發生異常。這個問題可能更多的是與編譯深度嵌套的Clojure代碼相比,具體使用cond

我能夠拿出下面這個宏,它接受一個子句列表,將它們包裝在thunk中,以提供使用if獲得的延期評估,然後使用some查找第一個邏輯真實測試表達式。由於創建了這麼多的匿名函數,它的性能可能並不是很好,但它會繞過堆棧溢出異常。

(defmacro cond' [& clauses] 
    `(:result 
    (some (fn [[pred-thunk# val-thunk#]] 
      (if (pred-thunk#) {:result (val-thunk#)})) 
      (partition 2 (list [email protected](map (fn [c] `(fn [] ~c)) clauses)))))) 

注意包裝,並在地圖返回值的展開,以確保some正確處理計算結果爲零值條款。

5

A cond與513條款不太可能在實踐中使用。

這是您的示例的功能實現。

(or (some identity (map #(if (= %1 %2) %1) 
        (range 1 1024) 
        (range 2 1025))) 
    1024) 
0

要求是這樣的:

  1. 給定的條件和結果的映射,例如列表

    [ [cond1 r1] [cond2 r2] ...],其中

    COND1:(= 1 1)

    R1:1

  2. 查找結果 - rn - 在condn計算結果爲true

解決方案使用some是完美的這裏如此存在這個問題,但我認爲我們可以通過避免使用宏

(defn match [[condition result]] 
    (when condition result)) 

(some match [[(= 1 2) 100] [(= 2 3) 200] [(= 3 3) 300]]) ;; => 300