Clojure中的多元循环使用

3

也许我有些急进了,我的第一天学习Clojure时才刚刚接触到函数,但我想要雄心勃勃地编写一个递归函数,将浮点数转换为三进制。如果我通过函数名调用函数而不是使用recur,它可以正常运行。我理解问题在于我只是在对函数的1元版本进行recur,在多元函数中如何处理递归的标准方法?我正在阅读的书似乎没有涉及这方面的内容。

(defn float-to-ternary
  ([x k s]
    (def a (int x))
    (def r (- x a))
    (def carry-string (str s (. Integer toString a 3)))
    (cond
      (== r 0) carry-string
      (> k 20) carry-string
      :default (recur (* 3 r) (inc k) carry-string)
    )
  )
  ([x]
    (def a (int x))
    (def r (- x a))
    (def carry-string (str (. Integer toString a 3) "."))
    (cond
      (== r 0) (str (. Integer toString a 3))
      :default (recur (* 3 r) 1 carry-string)
    )
  )
)

(. Integer toString a 3) ~= (Integer/toString a 3) - birdspider
1个回答

9
如果您想要“递归到不同的 arity”,只需直接调用函数而不是使用 `recur` 关键字:
(defn float-to-ternary
  ([x k s]
   (def a (int x))
   (def r (- x a))
   (def carry-string (str s (. Integer toString a 3)))
   (cond
     (== r 0) carry-string
     (> k 20) carry-string
     :default (recur (* 3 r) (inc k) carry-string)))   

  ([x]
   (def a (int x))
   (def r (- x a))
   (def carry-string (str (. Integer toString a 3) "."))
   (cond
     (== r 0) (str (. Integer toString a 3))
     :default (float-to-ternary (* 3 r) 1 carry-string))))

这是安全的。当你不使用recur时,你会“消耗”一个堆栈帧,但剩下的递归使用recur,所以没问题。


我也有一些必须的建议:

  • 除非你真的有充分的理由,否则不要在函数内部使用defdef会创建全局变量,在函数返回时仍然存在于作用域中!

  • 你使用cond是不必要的。

    • 在第一个主体中,你想为前两个条件返回carry-string。你可以把它们连接成一个条件,并用or连接,这样你就可以简单地使用if

    • 由于第二次使用只有两种结果,再次使用if更有意义。

考虑到这点,您的代码应该像这样:

(defn float-to-ternary
  ([x k s]
   (let [a (int x)
         r (- x a)
         carry-string (str s (. Integer toString a 3))]
     (if (or (> k 20) (== r 0))
      carry-string
      (recur (* 3 r) (inc k) carry-string))))

  ([x]
   (let [a (int x)
         r (- x a)
         carry-string (str (. Integer toString a 3) ".")]
     (if (== r 0)
       (str (. Integer toString a 3))
       (float-to-ternary (* 3 r) 1 carry-string)))))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接