문제

좋아요.나는 Clojure를 만지작거리고 있었고 계속해서 같은 문제에 직면했습니다.이 작은 코드 조각을 살펴보겠습니다.

(let [x 128]
  (while (> x 1)
    (do
      (println x)
      (def x (/ x 2)))))

이제 다음과 같이 128로 시작하는 시퀀스가 ​​인쇄될 것으로 예상됩니다.

128
64
32
16
8
4
2

대신 128을 계속해서 인쇄하는 무한 루프입니다.분명히 내가 의도한 부작용이 작동하지 않습니다.

그렇다면 이와 같은 루프에서 x 값을 어떻게 재정의해야 할까요?나는 이것이 Lisp와 같지 않을 수도 있다는 것을 알고 있습니다(아마도 자체적으로 재귀하는 익명 함수를 사용할 수 있습니다). 그러나 이와 같은 변수를 설정하는 방법을 찾지 못하면 미칠 것입니다.

내 다른 추측은 set!을 사용하는 것이지만 바인딩 형식이 아니기 때문에 "잘못된 할당 대상"이 표시됩니다.

이것이 어떻게 작동하는지 알려주십시오.

도움이 되었습니까?

해결책

def 일부 코드의 함수 또는 내부 루프에서 사용하더라도 Toplevel var를 정의합니다. 당신이 들어가는 것 let VARS가 아닙니다. 당 문서 let:

LET로 만든 현지인은 변수가 아닙니다. 일단 생성 된 가치는 결코 변하지 않습니다!

(내 강조가 아닙니다.) 당신은 여기서 당신의 예를 위해 변한 상태가 필요하지 않습니다. 당신은 사용할 수 있습니다 loop 그리고 recur.

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

당신이 화려하고 싶다면 당신은 명시 적을 피할 수 있습니다 loop 전적으로.

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
  (doseq [x xs] (println x)))

만약 너라면 진짜 Mutable State를 사용하고 싶었습니다 원자 작동 할 수도 있습니다.

(let [x (atom 128)]
  (while (> @x 1)
    (println @x)
    (swap! x #(/ %1 2))))

(당신은 필요하지 않습니다 do; while 당신을 위해 명백한 몸에 몸을 감싸십시오.) 정말, 정말 이 작업을 원했습니다 vars 당신은 이런 끔찍한 일을해야합니다.

(with-local-vars [x 128]
  (while (> (var-get x) 1)
    (println (var-get x))
    (var-set x (/ (var-get x) 2))))

그러나 이것은 매우 못 생겼으며 관용적 인 망토가 아닙니다. Clojure를 효과적으로 사용하려면 돌연변이 상태의 관점에서 생각을 중단해야합니다. 비 기능적 스타일로 Clojure 코드를 작성하려고 노력하는 것은 분명히 당신을 미치게 할 것입니다. 잠시 후에는 실제로 변수 변수가 실제로 얼마나 드물는지 즐거운 놀라움이 될 수 있습니다.

다른 팁

vars (당신이 "def"something)를 할 때 얻을 수있는 것입니다.

user=> (def k 1)
#'user/k
user=> k
1

당신이하는 것을 막는 것은 없습니다.

user=> (def k 2)
#'user/k
user=> k
2

스레드-로컬 정착 가능한 "장소"를 원한다면 "바인딩"및 "세트!"를 사용할 수 있습니다.

user=> (def j) ; this var is still unbound (no value)
#'user/j
user=> j
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0)
user=> (binding [j 0] j)
0

그러면 다음과 같은 루프를 쓸 수 있습니다.

user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil

그러나 나는 이것이 꽤 일체형이라고 생각합니다.

함수가 여전히 순수하게 유지되기 때문에 순수 함수에 변경 가능한 지역 변수를 갖는 것이 해를 끼치지 않는 멋지고 편리한 기능이라고 생각한다면 Rich Hickey가 언어에서 해당 변수를 제거한 이유를 설명하는 이 메일링 리스트 토론에 관심이 있을 것입니다. . 왜 변경 가능한 로컬이 아닌가?

관련 부분:

지역 주민이 변수인 경우, 즉돌연변이가 발생하면 폐쇄는 돌연변이 상태를 초과하여 폐쇄 될 수 있으며, 폐쇄가 탈출 할 수 있다는 점을 감안할 때 (동일하게도 추가 금지없이) 결과는 스레드-미트가 될 것입니다.그리고 사람들은 확실히 그렇게 할 것입니다클로저 기반 의사 객체.결과는 Clojure의 접근 방식에 큰 구멍이 될 것입니다.

돌연변이 가능한 현지인이 없으면 사람들은 기능적 루핑 구조 인 Recur를 사용해야합니다.처음에는 이상하게 보일 수 있지만, 이는 변이가 있는 루프처럼 간결하며, 결과 패턴은 다음과 같습니다 clojure의 다른 곳에서 재사용되는 경우, 즉반복, 축소, 변경, 출퇴근 등 는 모두 (논리적으로) 매우 유사합니다.비록 내가 감지하고 돌연변이 폐쇄가 빠져나가는 것을 방지하기 위해 다음과 같이 유지하기로 결정했습니다 를 사용하여 일관성을 유지하세요.가장 작은 컨텍스트에서도 비변이 루프는 다음과 같습니다 돌연변이보다 이해하고 디버깅하기 쉽습니다.어쨌든, Vars 는 적절한 경우 사용할 수 있습니다.

후속 게시물의 대부분은 구현에 관한 것입니다. with-local-vars 매크로;)

당신은 더 독창적으로 사용할 수 있습니다 iterate 그리고 take-while 대신에,

user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top