2009-11-10 80 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(或者您可以更改对它的调用以接受任意数量参数的函数)。