썸네일이 가리키는 것처럼, clojure에서 loop-recur
을 사용하면 고전적인 재귀 함수 호출과 동일한 형태와 효과가 나타납니다. 그것이 존재하는 유일한 이유는 순수 재귀보다 훨씬 효율적이라는 것입니다.
recur
은 꼬리 위치에서만 발생할 수 있으므로 loop
"변수"는 절대로 다시 필요하지 않습니다. 따라서 스택에 보존 할 필요가 없으므로 중첩 된 함수 호출과 달리 재귀 적 또는 비 순차적 스택이 사용되지 않습니다. 결과적으로 &은 다른 언어의 명령형 루프와 매우 유사하게 동작합니다. for
루프 모두 "변수", "변경"에 한정된다는 점이다 Java 스타일 비교
개선 만loop
식 초기화시 상기 recur
식 업데이트 때. vars에 대한 변경 사항은 루프 본문이나 다른 곳에서는 발생할 수 없습니다 (예 : Java에서 루프 변수를 변경할 수있는 포함 된 함수 호출).
"루프 바 (loop vars)"가 변이/업데이트 될 수있는 위치에 대한 이러한 제한 사항으로 인해 실수로 버그를 변경할 기회가 줄어 듭니다. 제한의 비용은 루프가 전통적인 Java 스타일 루프만큼 유연하지 않다는 것입니다.
이 비용 편익 교환이 다른 비용 편익 상충 관계보다 유용한 시점을 결정하는 것은 사용자의 몫입니다.당신은 순수 자바 스타일의 루프를 원하는 경우, 자바 변수를 시뮬레이션하기 Clojure에서 atom
사용하기 쉬운 :
; Let clojure figure out the list of numbers & accumulate the result
(defn fact-range [n]
(apply * (range 1 (inc n))))
(spyx (fact-range 4))
; Classical recursion uses the stack to figure out the list of
; numbers & accumulate the intermediate results
(defn fact-recur [n]
(if (< 1 n)
(* n (fact-recur (dec n)))
1))
(spyx (fact-recur 4))
; Let clojure figure out the list of numbers; we accumulate the result
(defn fact-doseq [n]
(let [result (atom 1) ]
(doseq [i (range 1 (inc n)) ]
(swap! result * i))
@result))
(spyx (fact-doseq 4))
; We figure out the list of numbers & accumulate the result
(defn fact-mutable [n]
(let [result (atom 1)
cnt (atom 1) ]
(while (<= @cnt n)
(swap! result * @cnt)
(swap! cnt inc))
@result))
(spyx (fact-mutable 4))
(fact-range 4) => 24
(fact-recur 4) => 24
(fact-doseq 4) => 24
(fact-mutable 4) => 24
심지어 우리가 적어도 각, 자바 가변 변수를 에뮬레이트하는 원자를 사용하는 마지막 경우 우리가 눈에 띄게 swap!
기능으로 표시 한 것을 돌연변이시켜 "우발적 인"돌연변이를 놓치기 어렵게 만듭니다.
P. spyx
을 사용하고 싶다면 in the Tupelo library
정신 모형과 관련하여 이름을 새로운 값으로 리 바인딩하는 것으로 재귀를 생각하십시오. 값은 여전히 변경 가능하지 않으며, 그 값 중 하나를 어딘가에 저장하면 원자, & c),'recur'가 원래의 것을 rebind하기 때문에 발생하지 않습니다. –