2014-10-29 3 views
0

Я пытаюсь познакомиться с Clojre, поэтому я решил сделать в нем проект интегратора Runge Kutta. Однако у меня возникают проблемы с неизменным характером заявления let. Я хочу оценивать 8 переменных на каждой итерации цикла и использовать их для повторения через нее, пока мой цикл не будет закончен.Рекурсивное переназначение переменных в Clojure

Как я понимаю, так как мой повторялся находится внутри пусть сфер «s, мой к и единицы не будет перезаписан с каждой рекурсией. Я ищу более идиоматический способ рекурсии через моего интегратора.

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

+0

Я не думаю, что ваше понимание правильное - привязки 'let' будут пересмотрены на каждой итерации цикла. Вы попробовали? – Alex

ответ

0

Сначала, как @Alex прокомментировал, то recur делает оценку ваших k с и l сек заново на каждой итерации loop. A recur относится к его немедленно включающей функции или форме цикла, в этом случае последней.


(println k1) предполагает, что то, что вы ищете последовательность k1 с, как вы итерацию вокруг loop.

Clojure имеет понятие ленивый sequence: потенциально бесконечную последовательность значений, которые

  • вычисленных при вызове
  • забытых, если/когда больше не доступен (мусор не собирает).

Вы должны

  • рама решение ленивой-последовательность k1 с и - в то время как мы на это -
  • сделать вашу evalg и evalf функцию аргументы вашей 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])))) 

Там вполне могут быть ошибки в этом, как я ощупью в темноте.

Последовательность бесконечна. Вы можете остановить его

  • , возвращая xn слишком, и обертывание результата в take-while или
  • работают, как может Итерацию вы хотите - это выглядит как (long (/ dx)), хотя может быть одно- там. Затем просто используйте nth или take, чтобы получить то, что вы хотите.

Дайте нам знать, как вы поживаете.