Clojure从递归调用返回值

时间:2012-04-22 03:19:55

标签: recursion clojure

我正在研究一个Clojure算法来解决这里提出的问题:http://spin.atomicobject.com/2011/05/31/use-clojure-to-move-drugs-a-programming-challenge/我遇到了打嗝。

我正在使用递归算法(也许不是开头的正确选择),走过一个按照从最高到最低的重量比排序的“玩偶”结构的向量。相关代码是:

(defn get-carryable-dolls 
  [dolls carryable-dolls]
  (def doll (first dolls)) ;initializing for use in multiple places
  (def rest-dolls (rest dolls)) ;initializing for use in multiple places
  (
    if (will-fit? doll (get-weight-sum carryable-dolls)) 
    ( ;will fit
      (
        if 
        (= carryable-dolls {})
        (def new-doll-set [doll]) ;First trip, get rid of empty set by initializing new
        (def new-doll-set (flatten [doll carryable-dolls])) ;otherwise, flatten set into vector of structs
      )
      ;tests to see if we have any more dolls to test, and if so, recurses. Otherwise, should pass the resultant vector
      ;up the stack. it appears to be the "else" potion of this if statement that is giving me problems.
      (if (not= () rest-dolls) (get-carryable-dolls rest-dolls new-doll-set) (vec new-dolls))
    )
    ( ;will not fit
      ;gets the rest of the dolls, and sends them on without modifying the vector of structs
      ;it appears to be the "else" potion of this if statement that is giving me problems.
      (if (not= () rest-dolls) (get-carryable-dolls rest-dolls carryable-dolls) (vec carryable-dolls))
    )
  )
)

该代码工作正常; returnable-dolls包含所需的玩偶结构矢量作为解决方案返回。不幸的是,当我尝试将returnable-dolls向量返回到调用位置时,我收到以下错误:

CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentVector,compiling:(drugmover\tests.clj:83)

第82-83行:

(def empty-dolls {})
(def designated-dolls (get-carryable-dolls sorted-values empty-dolls))

我对编译器错误中可能导致问题的原因感到困惑,并且由于Clojure似乎更喜欢简单的错误消息而不是堆栈跟踪(或者至少是Clooj中的REPL功能),我在失去了如何解决它。如果有人有任何建议,我将非常感谢他们!

提前致谢。

修改

我已经使用答案和评论中的建议修改了代码,并提供了一些注释来帮助说明正在进行的流控制。希望通过说明我的想法,有人能够让我知道我哪里出错了。

3 个答案:

答案 0 :(得分:4)

以下代码包含您在其他答案和其他答案中已收到的大部分建议,即:

  • 摆脱超级(和错误的)括号
  • 以更惯用的方式格式化代码
  • 使用loop / let代替def进行本地名称绑定
  • 使用seq检查空列表
  • 在添加元素之前删除对空carryable-dolls seq的不必要检查

如果没有你所拥有的辅助函数的定义(例如will-fit?),我无法测试它,但至少应修复一些问题(并以更易读的形式提供代码):< / p>

(defn get-carryable-dolls 
  [dolls carryable-dolls]
  (loop [doll (first dolls)
         rest-dolls (rest dolls)]
    (if (will-fit? doll (get-weight-sum carryable-dolls))
      (let [new-doll-set (if (seq carryable-dolls)
                           (cons doll carryable-dolls)
                           [doll])]
        (if (seq rest-dolls)
          (recur rest-dolls new-doll-set)
          (vec new-dolls)))
      (if (seq rest-dolls)
        (recur rest-dolls carryable-dolls)
        (vec carryable-dolls)))))

以下是利用标准reduce函数的代码的完整重构,并定义了一个函数,该函数提供核心决策逻辑,是否必须在结果中包含玩偶:

(defn add-if-fits [dolls doll]
  (if (will-fit? doll (get-weighted-sum dolls))
    (cons doll carryable-dolls)
    carryable-dolls))

(defn get-carryable-dolls [dolls carryable-dolls]
  (reduce add-if-fits carryable-dolls dolls))

答案 1 :(得分:3)

在这段代码中有太多方法,它们会导致问题。我强烈建议您按照其他人的方式格式化您的代码,这样可以轻松突出这样的错误。我甚至猜不出你要做什么,所以我不能重写整个片段,但需要注意的是if的语法是:

(if test then else)

在这些事情中不允许额外的任何事情:例如,(if true 1 2)很好,但是(if (true) 1 2)会尝试将true作为函数调用,并且因为它是布尔值而失败。如果您想要将表达式“分组”在一起并评估其副作用,则需要(do expr1 expr2),而不是(expr1 expr2)

答案 2 :(得分:3)

您的if代码周围有额外的问题,这就是导致该错误的原因:

下面的代码会产生相同的错误,因为它有额外的parens(在if中调用else部分来创建向量时的情况相同:

((vec {:a 10 :b 100}))

尝试在REPL中执行此操作,您将看到相同的异常:

java.lang.IllegalArgumentException: Wrong number of args (0) passed to: PersistentVector (NO_SOURCE_FILE:0)