Clojure:在 COND 中使用绑定,将重复项仅放置在尾部位置



我有一些代码目前给我一个错误,因为递归只能在尾部位置。 函数如下:

(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中做到这一点,或者如果可能的话,什么时候让和如果让似乎不这样做。修复我的代码以仅使用一个重复周期的最佳方法是什么?

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

1(如果没有循环语句,"recur"将重复回到defn。在我读过的书中,所有递归的例子都使用循环/递归,所以我认为有必要有一个循环。不是,我的循环语句想要多余。

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

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

(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没有问题 - 尾递归并不意味着它必须在文本的最后一行,只是当它返回时没有更多的计算要做。

主要问题是搞砸了括号(count后太多了(。 我建议找一个自动缩进的编辑器(并痴迷地使用自动缩进(。 这将立即显示括号问题(我在 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))))

在你最初的问题中,你问"我能想到的解决这个问题的一种方法是条件绑定,如果我能说:当计数小于零时,将计数运算符设置为"inc",否则设置为"dec",然后在最后重复。 没有人回答那部分:

(let [d (if (neg? degree) inc dec)]
  (recur (step scale) (d degree)))
除了你想在一种情况下调用 reverse,

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

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

尽管正如安德鲁·库克(Andrew Cooke(指出的那样,在每个"尾巴"中都有重复的简单cond(并且没有循环(就可以正常工作。

最新更新