我正在使用牛顿算法来计算第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))
)
但编译器抛出错误,导致下一个结果无法解决......现在发生了什么?
答案 0 :(得分:0)
此代码存在许多问题。为了向您提供一个工作示例,让我们重写root
和count_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
函数中:
loop
构造来定义循环的开始位置以及任何所需的定义,并使用recur
以新的方式执行循环的递归调用为定义指定的值。请注意,如果所有必要的定义都作为函数count-root
的参数给出,则不需要循环,递归可以返回到函数的开头。我建议您养成使用cond
代替if
的习惯。 if
函数看起来像整个很多,就像其他语言中的if
语句一样,这让我们(好吧,好吧, ME :-)经常陷入困境。问题是,当您使用if
时,您必须记住指定 BOTH “true”和“false”操作,因为if
表单不能“短” -切”。也就是说,如果在“其他”语言中如此,那么你总是要写:
如果有什么 然后是真实的东西 ELSE虚假的东西 ENDIF
或者,更多的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
函数中:
nth
和guess
定义函数。在count_root
的原始版本中,您使用了while
功能。使用while
要求其参数必须改变,这意味着它必须以某种方式处理原子;也就是说,它必须处理一个可修改的变量。大多数时候在Clojure中,通常被认为是“变量”的东西实际上无法改变。这是故意的。 Clojure从一开始就用于编写多线程应用程序,其中协调对“变量”的更改成为一个重要问题。如果实际上不能改变“变量”,则需要进行协调(通过诸如信号量,关键部分等构造)。因此,值的定义(如果它们不能改变或改变,它们不是真正的“变量”)默认是不变的。现在,任何“真正的”程序都需要随时间变化的值,而Clojure提供了这样的需求 - 但它不是默认值。您必须明确考虑需要修改的内容,并且需要以不同方式访问这些可修改的值。
现在,给你一个观察。你可能会发现用Clojure编程,或者用任何使你以不同于你习惯的方式做事的语言编程,一开始会觉得很奇怪,甚至可能很难。我知道这是我在学习新手入门系统时所经历的。很多时候,我们的思维过程进入“车辙”,任何迫使我们摆脱这些“车辙”的事情都让我们感到不舒服。但是,有时候在“不舒服”的时期里努力工作最终会让你学到有价值的东西。如果你坚持使用Clojure,你将学习一种不同的思考软件的方法 - 并且有多个观点来考虑问题本身就是一件好事。
当你学习新东西时,一本好的指南会很有帮助。我建议Clojure For The Brave And True,它的优势在于它的在线版本是免费的啤酒。
祝你好运。