2009-11-10 96 views
5

我最近開始閱讀Paul Grahams的On Lisp,並學習了clojure,所以這裏可能有一些非常明顯的錯誤,但我看不到它:(顯然,它是一個項目問題)這個Clojure程序有什麼問題?

(ns net.projecteuler.problem31) 

(def paths (ref #{})) 

; apply fun to all elements of coll for which pred-fun returns true 
(defn apply-if [pred-fun fun coll] 
    (apply fun (filter pred-fun coll))) 

(defn make-combination-counter [coin-values] 
    (fn recurse 
    ([sum] (recurse sum 0 '())) 
    ([max-sum current-sum coin-path] 
     (if (= max-sum current-sum) 
      ; if we've recursed to the bottom, add current path to paths 
      (dosync (ref-set paths (conj @paths (sort coin-path)))) 
      ; else go on recursing 
      (apply-if (fn [x] (<= (+ current-sum x) max-sum)) 
       (fn [x] (recurse max-sum (+ x current-sum) (cons x coin-path))) 
       coin-values))))) 

(def count-currency-combinations (make-combination-counter '(1 2 5 10 20 50 100 200))) 
(count-currency-combinations 200) 

當我運行在REPL的最後一行,我得到的錯誤:

<#CompilerException java.lang.IllegalArgumentException: Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> 

除了錯誤所在的問題,更有趣的問題是:如何將一個調試呢?錯誤消息不是很有幫助,我還沒有找到單步完成clojure代碼的好方法,而且每次遇到問題時我都無法真正地詢問堆棧溢出問題。

+0

優秀的問題。錯誤消息可能相當有敵意! – 2009-11-10 21:57:04

回答

13

三個提示,可能使你的生活在這裏更容易:

  1. Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> 告訴你大致發生錯誤:$fn在那裏的意思是匿名函數結束,它會告訴你它被宣佈裏面遞歸,這在make-combination-counter內宣佈。有兩個匿名函數可供選擇。

  2. 如果將源代碼保存在文件中,並將其作爲腳本執行,它將爲您提供帶有文件中行號的完整堆棧跟蹤。

    at net.projecteuler.problem31$apply_if__9.invoke(problem31.clj:7) 
    

    注意,您還可以通過檢查檢查從REPL中的最後一個異常和堆棧跟蹤* E如:(.stackTrace * E)的堆棧跟蹤起初很艱鉅,因爲它拋出了所有的Java內部。您需要學會忽略這些,只需查找引用您的代碼的行即可。這是你的情況很容易,因爲他們都開始net.projecteuler

  3. 您可以命名您的匿名功能,以幫助更快地識別它們:

    (fn check-max [x] (<= (+ current-sum x) max-sum)) 
    

在你的情況下,使用所有這些信息,你可以看到apply-if被單個參數函數傳遞爲fun。應用這樣做(f [1 2 3]) - >(f 1 2 3)。從你的評論你想要的是地圖。 (映射f [1 2 3]) - >(list(f 1)(f 2)(f 3))。當我用map替換apply時,該程序似乎工作。

最後,如果您想檢查值,您可能需要查看clojure-contrib.logging,它有一些助手來實現此效果。有一個間諜宏,它允許你包裝一個表達式,它將返回完全相同的表達式,所以它不會影響你的函數的結果,但會打印出EXPR = VALUE,這很方便。同樣在這個小組裏,不同的人已經發布了完整的跟蹤解決方案並且總有可靠的println。但是這裏的關鍵技巧是能夠準確識別爆炸事件。一旦你知道這通常是明確的原因,但是當你無法分辨輸入是什麼時,有時需要打印輸出。

2

不要對我有REPL,雖然它看起來像:

(defn apply-if [pred-fun fun coll] 
    (apply fun (filter pred-fun coll))) 

需要像'(1 2 3 4 5)列表過濾一些人出來'(1 3 5) ,然後創建像(fun 1 3 5)

一個函數調用,它看起來像它被稱爲(apply-if (fn [x]與函數,想要接收一個號碼列表作爲單個參數。

您可以將apply-if函數更改爲僅將呼叫傳遞給fun(或者您可以更改對它的調用以接受任意數量參數的函數)。