Clojure中变量的递归重新赋值

时间:2014-10-29 10:19:46

标签: recursion clojure immutability let

我试图更多地了解Clojre,所以我决定在其中使用Runge Kutta集成商项目。但是,我在使用 let 语句的不可变特性时遇到了问题。我想在循环的每次迭代中评估8个变量,并使用它们来递归它直到我的循环结束。

我理解它的方式,因为我的 recur 位于 let 的范围内,我的k和l&s赢了&每次递归都会覆盖#39;我正在寻找一种更惯用的方式来通过我的集成商进行递归。

(loop [psin 0  Xin 1 xn dx] ;initial values
  (if (> xn 1)
    psin
    (let [k1 (evalg  xn psin) ;define 4 variables to calculate next step of Xi, psi
          l1 (evalf  xn Xin)  ;evalf and evalg evaluate the functions as per Runge Kutta
          k2 (evalg (+ (* 0.5 dx) xn) (+ (* 0.5 l1) psin))
          l2 (evalf (+ (* 0.5 dx) xn) (+ (* 0.5 k1) Xin))
          k3 (evalg (+ (* 0.5 dx) xn) (+ (* 0.5 l2) psin))
          l3 (evalf (+ (* 0.5 dx) xn) (+ (* 0.5 k2) Xin))
          k4 (evalg (+ dx xn) (+ l3 psin))
          l4 (evalf (+ dx xn) (+ k3 Xin))]
      (do
        (let [Xinew (+ Xin (* (/ dx 6) (+ k1 k4 (* 2 k3) (* 2 k2))) )
              psinew (+ psin (* (/ dx 6) (+ l1 l4 (* 2 l2) (* 2 l3) )))]
          (println k1)
          (recur psinew Xinew (+ dx xn)))))))

非常感谢!期待更多地了解clojure:)

1 个答案:

答案 0 :(得分:0)

首先,正如@Alex已评论过,recur会在k的每次迭代中重新评估您的llooprecur指的是它的直接封闭函数或循环形式,在这种情况下是后者。


(println k1)表明,当你围绕k1进行迭代时,你正在寻找的是loop的序列。

Clojure有一个 lazy sequence 的概念:一个潜在的无穷无尽的值序列

  • 在被叫
  • 时计算
  • 忘记了/当不再可访问时(垃圾收集)。

你应该

  • 将解决方案构建为k1 s的惰性序列,并且 - 当我们在时 它 -
  • evalg制作evalfrunge-kutta个函数参数 功能

我们可以使用lazy-seq macro中包含的显式递归从头开始构建我们的延迟序列,但是有一个完整的序列函数库,其中一个通常表达你的意图。这些比你或我写的要快,但仍然比你的loop慢得多。

这里有一个方便的功能是iterate。我们可以系统地将您的loop转换为使用它,如下所示:

  • 将多个loop参数转换为单个destructured 向本地函数的向量参数(我称之为step)。
  • 在参数列表中包含您想要的任何结果,在本例中为k1
  • 重写迭代以返回下一组参数 作为向量的循环。
  • 在结果序列上调用map以获取所需数据。

我最好的猜测如下:

(defn runge-kutta [evalg evalf dx]
  (letfn [(step [[k1 psin  Xin xn]]
                (let [k1 (evalg  xn psin)
                      l1 (evalf  xn Xin)
                      k2 (evalg (+ (* 0.5 dx) xn) (+ (* 0.5 l1) psin))
                      l2 (evalf (+ (* 0.5 dx) xn) (+ (* 0.5 k1) Xin))
                      k3 (evalg (+ (* 0.5 dx) xn) (+ (* 0.5 l2) psin))
                      l3 (evalf (+ (* 0.5 dx) xn) (+ (* 0.5 k2) Xin))
                      k4 (evalg (+ dx xn) (+ l3 psin))
                      l4 (evalf (+ dx xn) (+ k3 Xin))
                      Xinew (+ Xin (* (/ dx 6) (+ k1 k4 (* 2 k3) (* 2 k2))) )
                      psinew (+ psin (* (/ dx 6) (+ l1 l4 (* 2 l2) (* 2 l3) )))]
                  [k1 psinew Xinew (+ dx xn)]))]
    (map first (iterate step [nil 0 1 dx]))))

这可能是错误的,因为我在黑暗中摸索。

序列无穷无尽。你可以阻止它

  • 也返回xn s,并将结果包装在take-while中 或
  • 计算出你想要的迭代方式 - 它看起来像(long (/ dx)), 虽然那里可能存在一次性错误。然后使用nthtake得到你想要的东西。

告诉我们您是如何继续前进的。