3

我有一些代码目前给我一个错误,因为 recur 只能在尾部位置。这是功能:

(defmethod transposer
    Long [scale degree]
    (loop [new-scale scale count degree]
    (cond 
        (zero? count) new-scale 
        (< count 0) (recur (step (reverse new-scale)) (inc count)))
        :else (recur (step new-scale) (dec count))))

我认为解决此问题的一种方法是条件绑定,如果我可以说: 当计数小于零时,将计数运算符设置为“inc”,否则设置为“dec”,然后在最后重复。

那么这将解决我的问题。但我不确定如何在 clojure 中执行此操作,或者是否可能,when-let 和 if-let 似乎没有执行此操作。将我的代码修复为仅使用一次重复的最佳方法是什么?

编辑:我在这里学到了几件事:

1) 如果没有循环语句,“recur”将返回到 defn。在我看的书中,所有recur的例子都使用了loop/recur,所以我认为有一个循环是很有必要的。不是,我的循环语句是多余的。

2)括号错误给了我一个令人困惑的错误,令我感到奇怪的是,两个 cond 语句都不会被认为是在尾部,因为它们是互斥的。我应该更多地关注我的paren完成检查器。

3)如果我确实想做条件绑定,我可以使用标准的“let”语句并在其中包含条件逻辑。来自 java 背景,我有时会忘记 clojure 在这方面所允许的灵活性。

4

2 回答 2

4
(defn foo [scale degree]
  (loop [new-scale scale count degree]
    (cond 
      (zero? count) new-scale 
      (< count 0) (recur (step (reverse new-scale)) (inc count))
      :else (recur (step new-scale) (dec count)))))

我认为更接近你想要的(它不会给出尾部错误;我使用 defn 只是为了独立测试)。

多个recurs-tail递归没有问题,并不意味着它必须在文本的最后一行,只是它返回时没有更多的计算可做。

主要问题是弄乱了括号(之后太多count)。我建议找一个自动缩进的编辑器(并且痴迷地使用自动缩进)。那会立即显示parens问题(我在intellij idea中使用la clojure插件,但我相信其他人也有类似的功能)。

更新:为什么loop需要?

(defn foo [scale degree]
  (cond 
    (zero? degree) scale 
    (< degree 0) (recur (step (reverse scale)) (inc degree))
    :else (recur (step scale) (dec degree))))
于 2012-05-09T01:20:27.903 回答
3

在您最初的问题中,您问“我认为解决此问题的一种方法是条件绑定,如果我可以说:当计数小于零时,将计数运算符设置为“inc”,否则设置为“dec”,然后重复在最后。” 没有人回答那部分:

(let [d (if (neg? degree) inc dec)]
  (recur (step scale) (d degree)))

除了你想在一种情况下调用 reverse 而不是另一种情况,所以你也需要一个条件绑定。这是一个使用解构绑定的示例:

(let [[s d] (if (neg? degree) [reverse inc] [identity dec])]
  (recur (step (s scale)) (d degree)))

尽管正如 Andrew Cooke 指出的那样,在每个“尾部”(并且没有循环)中使用 recur 的简单 cond 工作得很好。

于 2012-05-09T02:12:39.680 回答