我有一些代码正在给我一个错误,因为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))))
我能想到解决这个问题的一种方法是条件绑定,如果我可以说: 当count小于零时,将count-operator设置为“inc”,否则设置为“dec”,然后在最后重复。
然后这会解决我的问题。但是我不知道如何在clojure中做到这一点,或者如果它甚至可能,那么 - let和if-let似乎不会这样做。修复我的代码只使用一次重复的最佳方法是什么?
编辑:我在这里学到的一些事情:1)如果没有循环语句,“recur”将重新返回到defn。在我读过的书中,所有重复使用循环/重复的例子,所以我认为有必要有一个循环。不是,我的循环声明想要多余。
2)括号错误给了我一个令人困惑的错误,让我感到奇怪的是,两个cond语句都不会被认为是尾部,因为它们是相互排斥的。我应该多关注我的完成检查。
3)如果我确实想要进行条件绑定,我可以使用标准的“let”语句,并在那里包含条件逻辑。来自java背景,我有时会忘记clojure在这个领域允许的灵活性。
答案 0 :(得分: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只是为了单独测试)。
多个recur
s没有问题 - 尾递归并不意味着它必须在文本的最后一行,只是在它返回时没有更多的计算要做。
主要问题是搞砸了(count
之后太多了)。我建议让一个自动缩进的编辑器(并且使用自动缩进)。这会立即显示出问题(我在intellij中使用了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))))
答案 1 :(得分:3)
在你的原始问题中,你问过“我能想到的一种解决方法是条件绑定,如果我可以说:当count小于零时,将count-operator设置为”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所指出的那样,在每个'尾'(并且没有循环)中复活的简单cond工作正常。