这个Clojure计划有什么问题?

时间:2009-11-10 19:30:46

标签: clojure

我最近开始阅读Paul Grahams的'On Lisp',并学习学习clojure,所以这里可能有一些非常明显的错误,但我看不出来:(显然是一个项目的欧拉问题)< / p>

(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代码,每次遇到问题时我都无法真正询问堆栈溢出。

2 个答案:

答案 0 :(得分:13)

可能会让您的生活更轻松的三个提示:

  1. Wrong number of args passed to: problem31$eval--25$make-combination-counter--27$recurse--29$fn (NO_SOURCE_FILE:0)> 告诉你大概发生错误的地方:$fn最后那里意味着匿名函数,它告诉你它是在recurse里面声明的,它是在make-combination-counter内声明的。有两种匿名函数可供选择。

  2. 如果您将源代码保存在文件中并将其作为脚本执行,它将为您提供包含文件中行号的完整堆栈跟踪。

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

    注意你也可以通过检查* e例如:(。stackTrace * e)检查REPL中的最后一个异常和堆栈跟踪。堆栈跟踪起初非常艰巨,因为它会抛出所有Java内部。您需要学会忽略它们,只需查找引用代码的行。这种情况非常简单,因为它们都以net.projecteuler

  3. 开头
  4. 您可以命名匿名函数以帮助更快地识别它们:

    (fn check-max [x] (<= (+ current-sum x) max-sum))
    
  5. 在您使用所有这些信息的情况下,您可以看到apply-if正在传递单个参数函数作为乐趣。申请这样做(f [1 2 3]) - &gt; (f 1 2 3)。从你的评论你想要的是地图。 (地图f [1 2 3]) - &gt; (列表(f 1)(f 2)(f 3))。当我用apply替换map时,该程序似乎有效。

    最后,如果你想检查值,你可能想要查看clojure-contrib.logging,它有一些帮助这种效果。有一个间谍宏允许你包装一个表达式,它将返回完全相同的表达式,因此它不会影响你的函数的结果,但会打印出EXPR = VALUE,这可以很方便。同样在该组织中,各种人都发布了完整的跟踪解决方案。总是有可靠的println。但这里的关键技能是能够确切地确定爆炸的是什么。一旦你知道通常清楚为什么,但有时候当你无法确定输入是什么时需要打印输出。

答案 1 :(得分: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(不带apply),或者可以更改对它的调用以获取一个带有任意数量参数的函数。