Clojure - 第n个根算法 - while-do循环

时间:2018-05-03 19:44:46

标签: loops clojure

我正在使用牛顿算法来计算第n个根,并且我遇到了非工作循环的问题。这是代码:

(defn root [nth guess]
  (if (<= guess 0) "Root doesn't exists" (count_root nth guess))
)

(defn count_root nth guess [nth guess]
  (def result guess)
  (def last_result result)
  (def temp (power nth result))
  (while (> (absolute (- result last_result)) 0.01)
         (do
           (def last_result result)
           (def result ('fn [nth result guess temp] (* (/ 1.0 nth) (+ (* (- nth 1) result) (/ guess temp)))))
           (def temp (power nth result))
         )
  )
  (str "Result: " result)
)

(defn power [nth result]
  (* result (- nth 1))
)

(defn absolute [x]
  (if (>= x 0) x (- x))
)

当我注释掉(而......)行时,它会计算一个循环传递并且结果是正确的。但是当包含(while ...)行时,下面的所有代码都会被忽略。

我已经将上面的代码重建为以下内容:

(defn power [nth result]
  (* result (- nth 1))
)

(defn absolute [x]
  (if (>= x 0) x (- x))
)

(defn is-good? [prev-result result]
  (< (absolute (- prev-result result)) 0.01)
)

(defn improve [nth result temp]
  (* (/ 1.0 nth) (+ (* (- nth 1) result) (/ result temp)))
)

(defn count-root [nth number]
  (loop [result number
         prev-result result
         temp (power nth result)]
       (let [next-result (* (/ 1.0 nth) (+ (* (- nth 1) result) (/ number temp)))])
       (if (is-good? (result next-result)) result (recur next-result)))
)

(defn root [nth number]
  (if (<= number 0) "Root doesn't exists" (count-root nth number))
)

但编译器抛出错误,导致下一个结果无法解决......现在发生了什么?

1 个答案:

答案 0 :(得分:0)

此代码存在许多问题。为了向您提供一个工作示例,让我们重写rootcount_root函数:

(defn power [nth result]
  (* result (- nth 1)))

(defn count-root [nth guess]
  (loop [result       guess
         last_result  guess
         temp         (power nth result)]
    (cond
      (> (Math/abs (- result last_result)) 0.01)
        (let [next-result (* (/ 1.0 nth) (+ (* (- nth 1) result) (/ guess temp)))]
          (recur next-result
                 result
                 (power nth next-result)))
      :else
        (str "Result: " result))))

(defn root [nth guess]
  (cond
    (<= guess 0)  "Root doesn't exists"
    :else         (count-root nth guess)))

让我们看看这里发生了什么变化,并尝试解释它。

首先,在count-root函数中:

  1. 我建议你不要在名字中使用下划线。最常用的Clojure约定是使用破折号分隔函数和定义名称的元素。
  2. 要执行带定义的循环,请使用loop构造来定义循环的开始位置以及任何所需的定义,并使用recur以新的方式执行循环的递归调用为定义指定的值。请注意,如果所有必要的定义都作为函数count-root的参数给出,则不需要循环,递归可以返回到函数的开头。
  3. 我建议您养成使用cond代替if的习惯。 if函数看起来像整个很多,就像其他语言中的if语句一样,这让我们(好吧,好吧, ME :-)经常陷入困境。问题是,当您使用if时,您必须记住指定 BOTH “true”和“false”操作,因为if表单不能“短” -切”。也就是说,如果在“其他”语言中如此,那么你总是要写:

    如果有什么   然后是真实的东西   ELSE虚假的东西 ENDIF

  4. 或者,更多的Clojurist时尚

    (if something
      true-stuff
      false-stuff)
    

    在Clojure中你不应该写

    (if something
      true-stuff)
    

    因为我发现这不能正常工作。如果您调用if函数并指定 true参数,就像您在“其他”编程语言中经常做的那样,它将编译并运行,但您将会得到奇怪的结果,不会发生的事情会发生,通常你会感到困惑,直到你意识到你有if没有“假”的一半或类似的东西。省去一些麻烦 - 使用cond代替if。 4.我建议使用Math/abs而不是定义自己的absolute函数。 5.使用let表单定义临时值,您不需要在表单内循环。在这里,我将next-result定义为临时值,我将在下一个表单中使用它。 6.使用recur以递归方式“跳转”到下一个最早的递归点。这里下一个最早的递归点是loop形式。如果我们没有这个loop形式或其他递归点,count-root的函数定义将作为递归点。什么时候

    接下来,在root函数中:

    1. 原始版本中函数的定义不正确。我在这里清理了它,以便用两个参数nthguess定义函数。
    2. 修改

      count_root的原始版本中,您使用了while功能。使用while要求其参数必须改变,这意味着它必须以某种方式处理原子;也就是说,它必须处理一个可修改的变量。大多数时候在Clojure中,通常被认为是“变量”的东西实际上无法改变。这是故意的。 Clojure从一开始就用于编写多线程应用程序,其中协调对“变量”的更改成为一个重要问题。如果实际上不能改变“变量”,则需要进行协调(通过诸如信号量,关键部分等构造)。因此,值的定义(如果它们不能改变或改变,它们不是真正的“变量”)默认是不变的。现在,任何“真正的”程序都需要随时间变化的值,而Clojure提供了这样的需求 - 但它不是默认值。您必须明确考虑需要修改的内容,并且需要以不同方式访问这些可修改的值。

      现在,给你一个观察。你可能会发现用Clojure编程,或者用任何使你以不同于你习惯的方式做事的语言编程,一开始会觉得很奇怪,甚至可能很难。我知道这是我在学习新手入门系统时所经历的。很多时候,我们的思维过程进入“车辙”,任何迫使我们摆脱这些“车辙”的事情都让我们感到不舒服。但是,有时候在“不舒服”的时期里努力工作最终会让你学到有价值的东西。如果你坚持使用Clojure,你将学习一种不同的思考软件的方法 - 并且有多个观点来考虑问题本身就是一件好事。

      当你学习新东西时,一本好的指南会很有帮助。我建议Clojure For The Brave And True,它的优势在于它的在线版本是免费的啤酒。

      祝你好运。