def를 사용하여 변수를 재정의 할 수 있다면 어떻게 불변으로 간주됩니까?


10

Clojure를 배우려고 노력하면 Clojure가 불변 데이터에 관한 모든 방법을 지속적으로 알 수는 없습니다. 그러나 def오른쪽 을 사용하여 변수를 쉽게 재정의 할 수 있습니까? Clojure 개발자가 이것을 피할 수는 있지만 모든 언어의 변수를 동일하게 변경하는 것을 피할 수 있습니다. 누군가가 이것이 어떻게 다른지 설명해 줄 수 있습니다. 왜냐하면 내가 읽고있는 튜토리얼과 책에서 그것을 놓친 것 같습니다.

예를 들어

a = 1
a = 2

Ruby에서 (또는 원하는 경우 blub)

(def a 1)
(def a 2)

클로저에서?

답변:


9

이미 알고 있듯이 Clojure에서 가변성이 권장되지 않는다고해서 이것이 금지되어 있고이를 지원하는 구조가 없다는 의미는 아닙니다. 따라서 def다른 언어에서 할당하는 것과 비슷한 방식으로 환경에서 바인딩을 변경하거나 변경할 수 있습니다 ( vars의 Clojure 설명서 참조 ). 글로벌 환경에서 바인딩을 변경하면 이러한 바인딩을 사용하는 데이터 오브젝트도 변경됩니다. 예를 들면 다음과 같습니다.

user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101

에 대한 바인딩을 재정의 한 후 본문에서 해당 바인딩을 사용하므로 x함수 f도 변경되었습니다.

변수를 재정의해도 기존 바인딩이 삭제되지 않고 그림자 만 표시되는 언어와 비교해보십시오 . 즉, 새 정의 다음에 오는 범위에서는 보이지 않습니다. SML REPL에서 동일한 코드를 작성하면 어떻게되는지 확인하십시오.

- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int

의 두 번째 정의 후에도 x함수 는 정의 될 때 범위에 f있던 바인딩 x = 1을 계속 사용합니다 . 즉, 바인딩 val x = 100이 이전 바인딩을 겹쳐 쓰지 않습니다 val x = 1.

결론 : Clojure를 사용하면 글로벌 환경을 변경하고 바인딩을 재정의 할 수 있습니다. SML과 같은 다른 언어와 마찬가지로 이것을 피할 수는 있지만 defClojure 의 구문은 글로벌 환경에 액세스하고 변경하는 것을 의미합니다. 실제로 이것은 Java, C ++, Python과 같은 명령형 언어에서 할당이 할 수있는 것과 매우 유사합니다.

그럼에도 불구하고 Clojure는 돌연변이를 피하는 많은 구성과 라이브러리를 제공하며 전혀 사용하지 않고도 먼 길을 갈 수 있습니다. 돌연변이를 피하는 것은 Clojure에서 선호되는 프로그래밍 스타일입니다.


1
돌연변이를 피하는 것이 훨씬 선호되는 프로그래밍 스타일입니다. 이 말은 요즘 모든 언어에 적용 할 것을 제안합니다. Clojure뿐만 아니라;)
David Arno

2

Clojure는 불변 데이터에 관한 것입니다

Clojure에 대해입니다 관리 (즉, 돌연변이 포인트를 제어하여 변경 가능한 상태 Ref이야, Atom이야, AgentS 및 VarS). 물론 interop을 통해 사용하는 Java 코드는 원하는대로 수행 할 수 있습니다.

그러나 def를 사용하여 변수를 쉽게 재정의 할 수 있습니까?

Var예를 들어 지역 변수와 달리 a를 다른 값에 바인드하려면 예입니다. 에서 언급 한 바와 같이 사실, 바르 및 지구 환경 , Var의 구체적 Clojure에서의 4 개의 "참조 타입"중 하나로 포함되어 있습니다 (나는 그들이 주로 참조하고 말하고 싶지만하지만 동적 Var 의 존재).

Lisp를 통해 REPL을 통해 대화식 탐색 프로그래밍 활동을 수행 한 오랜 역사가 있습니다. 이것은 종종 새로운 변수와 함수를 정의하고 오래된 것을 재정의하는 것을 포함합니다. 그러나 REPL 외부에서 defa를 바꾸는 Var것은 형편없는 것으로 간주됩니다.


1

에서 멋진과 진정한위한 Clojure의

예를 들어, Ruby에서 변수에 값을 여러 개 지정하여 값을 빌드 할 수 있습니다.

severity = :mild
  error_message = "OH GOD! IT'S A DISASTER! WE'RE "
  if severity == :mild
    error_message = error_message + "MILDLY INCONVENIENCED!"
  else
    error_message = error_message + "DOOOOOOOMED!"
  end

Clojure에서 비슷한 일을하고 싶을 수도 있습니다.

(def severity :mild)
  (def error-message "OH GOD! IT'S A DISASTER! WE'RE ")
  (if (= severity :mild)
      (def error-message (str error-message "MILDLY INCONVENIENCED!"))
  (def error-message (str error-message "DOOOOOOOMED!")))

그러나 이와 같은 이름과 관련된 값을 변경하면 이름과 관련된 값 또는 해당 값이 변경된 이유를 알기가 더 어렵 기 때문에 프로그램의 동작을 이해하기가 더 어려워 질 수 있습니다. Clojure에는 변경을 처리하기위한 도구 세트가 있으며, 10 장에서 배우게됩니다. Clojure를 배우면서 이름 / 값 연관을 변경할 필요는 거의 없습니다. 앞의 코드를 작성할 수있는 방법은 다음과 같습니다.

(defn error-message [severity]
   (str "OH GOD! IT'S A DISASTER! WE'RE "
   (if (= severity :mild)
     "MILDLY INCONVENIENCED!"
     "DOOOOOOOMED!")))

(error-message :mild)
  ; => "OH GOD! IT'S A DISASTER! WE'RE MILDLY INCONVENIENCED!"

루비에서 똑같은 일을 쉽게 할 수 없었습니까? 주어진 제안은 단순히 값을 반환하는 함수를 정의하는 것입니다. 루비에도 기능이 있습니다!
Evan Zamir

예, 알아요 그러나 제안 된 문제 (예 : 바인딩 변경)를 해결하는 필수 방법을 권장하는 대신 Clojure는 곰팡이 패러다임을 채택합니다.
Tiago Dall'Oca
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.