【问题标题】:Recursive Reassignment of Variables in ClojureClojure 中变量的递归重新分配
【发布时间】:2014-12-25 00:06:54
【问题描述】:

我正在尝试更多地了解 Clojre,因此我决定在其中进行我的 Runge Kutta 集成器项目。但是,我在处理 let 语句的不可变性质时遇到了问题。我想在循环的每次迭代中评估 8 个变量,并使用它们来递归它,直到我的循环完成。

按照我的理解,由于我的 recurlet 的范围内,我的 k 和 l 不会被每次递归覆盖。我正在寻找一种更惯用的方式来通过我的积分器进行递归。

(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:)

【问题讨论】:

  • 我认为您的理解不正确 - let 绑定将在每次循环迭代时重新评估。你试过了吗?

标签: recursion clojure immutability let


【解决方案1】:

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


(println k1) 表明您正在寻找的是k1s 的序列,因为您循环遍历loop

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

  • 在调用时计算
  • 如果/当不再可访问时忘记(垃圾收集)。

你应该

  • 将解决方案构建为k1s 的惰性序列,并且 - 当我们在 它——
  • evalgevalf 函数参数设置为runge-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]))))

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

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

  • 也返回xns,并将结果包装在take-while中 或
  • 计算出你想要的迭代次数 - 看起来像 (long (/ dx)), 尽管那里可能存在一次性错误。然后只需使用nthtake 得到你想要的。

让我们知道您的进展情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    • 2021-02-04
    • 2015-02-09
    • 2020-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多