2012-05-16 2 views
13

let 바인딩을 구현하는 2 가지 방법을 볼 수 있습니다. 먼저, SICP으로부터 알려진 바와 같이, let람다 함수으로 구현 될 수있다. 이것은 편리하고 간단하지만 각 람다 (fn)가 JVM에서 별도의 클래스로 변환되고 평균 프로그램에서 let의 수가 사용된다는 사실을 고려하면 매우 비싸 보입니다.Clojure에서`let '을 구현하는 방법과 그 오버 헤드는 무엇입니까?

두 번째로, let 바인딩은 로컬 Java 변수으로 직접 변환 될 수 있습니다. 오버 헤드는 거의 발생하지 않지만 스택에 바인딩을 저장하면 언어 의미가 깨집니다.이 경우 클로저 생성은 불가능합니다. 저장된 값은 스택을 푸는 즉시 삭제됩니다.

그래서 Clojure에서 실제로 사용되는 구현은 무엇입니까? Clojure 소스의 해당 줄을 가리키는 것이 좋습니다.

답변

16

let - 바운드 변수는 최종 로컬 값으로 스택에 저장됩니다.

최종적이므로 필요한 경우 클로저에 바인딩 할 수 있습니다 (Java의 익명 내부 클래스에서 최종 로컬 변수를 사용하는 방법과 유사합니다). 내부적으로 JVM은 값을 클로저를 나타내는 객체로 복사합니다 (최종 필드로 저장 됨). 따라서 스택 프레임이 사라진 후에도 클로저가 계속 작동합니다.

전체적으로 let-bound 변수는 매우 낮은 오버 헤드이므로 성능 측면에서 볼 때 전혀 주저하지 않아야합니다. 아마도 JVM에서 더 잘 수행 할 수 없습니다.

+0

그래서 필요한 경우에만 클로저 객체가 생성되고 (그리고 최종 변수가 복사 됨) 단순한 경우에 대해 vars가 스택에만 머물러 있다고 생각합니까? – ffriend

+0

예, 그것이 작동하는 방식은 거의 같습니다. 관심이 있으시면 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java를 방문하십시오. (Clojure의 컴파일러가하고있는 "마술"이 많이 있습니다.) – mikera

+0

@ffriend Java는 메소드 범위와는 달리 블록 범위 변수를 가지고 있습니다. 나는 Clojure 컴파일러가하는 것을 보지 않았지만, * let *은 최종 변수를 포함하는 코드 블록에 의해 간단하게 구현 될 수 있다고 생각한다. 왜 폐쇄로 구현 될 것이라고 생각하는지 모르겠습니다. –

1

로컬 변수는 스택에 할당되어 힙의 값/개체를 가리 킵니다. 포인터는 범위를 벗어나지 만 클로저가 포인터를 포인터로 유지하는 한 객체는 활성 상태로 유지됩니다.

관련 문제