如何将while循环转换为clojure代码

时间:2013-08-28 12:39:56

标签: clojure

例如,扩展的欧几里德算法(引自wiki):

function extended_gcd(a, b)
    x := 0    lastx := 1
    y := 1    lasty := 0
    while b ≠ 0
        quotient := a div b
        (a, b) := (b, a mod b)
        (x, lastx) := (lastx - quotient*x, x)
        (y, lasty) := (lasty - quotient*y, y)       
    return (lastx, lasty)

我试过并得到了:

 (defn extended-gcd 
  [a b]
  (loop [a a b b x 0 y 1 lx 1 ly 0]
     (if (zero? b)
      [lx ly]
      (recur b (mod a b)
             (- lx (* (int (/ a b)) x))
             (- ly (* (int (/ a b)) y))
             x y))))

我想我可以找到一种方法来翻译处理序列的循环。但是这个怎么样?我怎么用clojure方式写它?与map,reduce等有关的东西,而不是循环重复。

2 个答案:

答案 0 :(得分:5)

对于扩展的欧几里得算法,您可以使用简单的递归,这使得函数看起来非常优雅:

(defn extended-gcd [a b]
  (if (zero? b) [1 0]
    (let [[q r] [(quot a b) (rem a b)]
          [s t] (extended-gcd b r)] 
      [t (- s (* q t))])))

我们试一试:

user=> (extended-gcd 120 23)
[-9 47]

并非所有问题都需要才能使用map / reduce / sequence解决。我认为上述内容与Clojure一样,是您正在寻找的“(reduce + [1 2 3 4 5])”类型的答案。

答案 1 :(得分:0)

对于这种问题, iterate 通常是使用 loop 的一个很好的替代方法。在这种情况下,它会导致源算法的相当透明的翻译:

(defn extended-gcd [a b]
  (->> {:a a, :b b, :x 0, :y 1, :lx 1, :ly 0}
    (iterate 
      (fn [{keys [a b x y lx ly]}]
        (let [q (quot a b)]
          {:a b, :b (mod a b), :x (- lx (* q x)), :lx x, :y (- ly (* q y)), :ly y})))
    (drop-while #(not= 0 (:b %)))
    first
    ((juxt :lx :ly))))

也就是说,使用 loop 也是一种Clojure方式 - 我相信,避免使用它的劝告是为了鼓励使用更高级别的结构,而这些结构更合适。