2012-03-27 1 views
11

모든 예는 SICP 책에서 발췌 :클로저에 중첩 된 정의 문 (스키마와 같은)을 작성하는 표준 방법은 무엇입니까?

http://sicpinclojure.com/?q=sicp/1-3-3-procedures-general-methods은 LISP에 MIT 비디오 시리즈에서 동기가되었다 - 계획에서 http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/2a-higher-order-procedures/

, 다른 '정의'안에 넣어 '정의'할 수

(defn close-enough? [v1 v2] 
    (let [tolerance 0.00001] 
     (< (Math/abs (- v1 v2)) 
      tolerance))) 
: Clojure의에서
(define (close-enough? v1 v2) 
    (define tolerance 0.00001) 
    (< (abs (- v1 v2)) tolerance)) 

는 그것이 중첩되는 유일한 차이점과 문을 '하자'가있다 0

그러나이 같은 큰 Clojure의 무언가 재 작성에 대한 최신

(define (sqrt x) 
    (define (fixed-point f first-guess) 
    (define (close-enough? v1 v2) 
     (define tolerance 0.00001) 
     (< (abs (- v1 v2)) tolerance)) 
    (define (try guess) 
     (let ((next (f guess))) 
     (if (close-enough? guess next) 
      next 
      (try next)))) 
    (try first-guess)) 
    (fixed-point (lambda (y) (average y (/ x y))) 
      1.0)) 

이 사실 일을 수행하지만 매우 파격적인 모습 ...

(defn sqrt [n] 
    (let [precision  10e-6 
     abs    #(if (< % 0) (- %) %) 
     close-enough? #(-> (- %1 %2) abs (< precision)) 
     averaged-func #(/ (+ (/ n %) %) 2) 
     fixed-point  (fn [f start] 
          (loop [old start 
            new (f start)] 
           (if (close-enough? old new) 
            new 
            (recur new (f new)))))] 

     (fixed-point averaged-func 1))) 

(sqrt 10) 

월을? : 무엇을/

/2012 (8)

답변 해 주셔서 감사합니다.

기본적으로 'letfn'은 'let'과 크게 다르지 않습니다 - 호출되는 함수는 'letfn'정의에 중첩되어야합니다 (Scheme과는 반대로 함수는 정의 후에 다음 sexp에서 사용되며 정의 된 최상위 레벨 기능의 범위 내에 존재 함).

또 다른 질문은 ... 왜 어떤 계획을 수행하는 능력을 부여하지 않는가? 그것은 일종의 언어 디자인 결정입니까? 내가 계획 조직에 대해 좋아하는 것은 :

  • 1) 아이디어의 캡슐화 그래서 프로그래머로 작은 블록이 활용되고있는 것을 더 큰 블록에 같은 생각을 가지고 - 나는 단지 조금 사용하고, 특히 작은 블록이 큰 블록 내에서 한 번 차단됩니다 (작은 블록이 그 자체로 유용하더라도).

  • 2) 이것은 최종 사용자에게 유용하지 않은 거의 절차없이 네임 스페이스를 오염시키지 않습니다. (clojure 프로그램을 작성하고 일주일 후에 다시 돌아 왔으며 코드를 다시 배워야했습니다. 편평한 구조에서 그리고 나는 위로 아래로 방법에서와 반대로 나가 안쪽으로 부호를 안으로보고 있었다는 것을 느꼈다.

  • 3) 일반적인 메소드 정의 인터페이스에서 특정 하위 메소드를 추출하고 테스트하지 않고 들여 쓰기하여 변경된 버전을 붙여 넣을 수 있습니다.

왜 이것을 클로저에 구현하지 않습니까?

+1

흥미로운 질문이지만 나는 모든 것을 하나의 기능에 넣으려고 할 것이라고 생각하지 않습니다. – Kevin

답변

15

클로저에 중첩 된 이름 지정된 프로 시저를 작성하는 표준 방법은 letfn을 사용하는 것입니다.

제쳐두고 중첩 된 함수의 사용 예는 꽤 의심 스럽습니다. 예제의 모든 함수는 최상위 수준의 비 지역 함수가 될 수 있습니다. 그 이유는 함수가 자체적으로 더 유용하거나 덜 유용하기 때문이며 서로를 제외하고는 닫히지 않기 때문입니다.

+0

그 점에 감사드립니다! 업데이트 된 질문을보십시오! – zcaudate

11

모든 기능에서이 기능을 배치하는 것을 비판하는 사람들은 왜 그러한 방식으로 수행되었는지에 대한 내용을 이해하지 못합니다.예제가있는 SICP는 모듈의 개념을 설명하려고했지만 기본 언어에 다른 구문을 추가하지 않았습니다. 그래서 "sqrt"는 하나의 인터페이스를 가진 모듈이고, 나머지는 그 모듈 내의 로컬 또는 개인 함수입니다. 이것은 내가 믿는 R5RS 체계에 기초를 두었고 이후의 계획은 이후 내가 생각하는 표준 모듈 구조를 추가했다 (?). 그러나 관계없이, 구현을 숨기는 원칙을 더 보여줍니다.

노련한 스키마는 또한 중첩 된 로컬 함수의 비슷한 예제를 통해 수행되지만 대개 구현을 숨기고 값을 닫는데도 사용됩니다.

비록 이것이 교육적인 예가 아니었지만, 이것이 매우 가벼운 모듈 인 것을 볼 수 있었고, 나는 더 큰 "실제"모듈에서 이런 식으로 작성했을 것입니다. 재사용은 계획대로라면 괜찮습니다. 그렇지 않으면 나중에 필요할 때 사용할 수없는 기능을 노출하는 것일뿐 아니라 예기치 않은 사용 사례로 인해 나중에 기능을 손상시킬 수있는 기능에 부담을줍니다.

4

letfn이 표준 방법입니다.

하지만 Clojure는 Lisp이기 때문에 원하는 의미를 (거의) 만들 수 있습니다. 다음은 defineletfn의 관점에서 정의하는 개념 증명입니다.

(defmacro define [& form] 
    (letfn [(define? [exp] 
      (and (list? exp) (= (first exp) 'define))) 
      (transform-define [[_ name args & exps]] 
      `(~name ~args 
       (letfn [[email protected](map transform-define (filter define? exps))] 
       [email protected](filter #(not (define? %)) exps))))] 
    `(defn [email protected](transform-define `(define [email protected]))))) 

(define sqrt [x] 
    (define average [a b] (/ (+ a b) 2)) 
    (define fixed-point [f first-guess] 
    (define close-enough? [v1 v2] 
     (let [tolerance 0.00001] 
     (< (Math/abs (- v1 v2)) tolerance))) 
    (define tryy [guess] 
     (let [next (f guess)] 
     (if (close-enough? guess next) 
      next 
      (tryy next)))) 
    (tryy first-guess)) 
    (fixed-point (fn [y] (average y (/ x y))) 
       1.0)) 

(sqrt 10) ; => 3.162277660168379 

실제 코드를 들어, 당신은 더 많은 R5RS 같은 행동 define을 변경하려는 것 : 비 FN 값을 허용, defmacro는, let, letfnfn, 그리고 내부 정의를 확인, defn에서 일을 사용할 수 둘러싸는 시체의 시작 부분에 있습니다.

참고 : try에서 tryy으로 이름을 바꾸어야했습니다. 외관상으로는 try은 재정의가 자동으로 실패하는 특별한 비 함수, 비 매크로 구문입니다.

+0

매우 시원합니다. 나는 당신의 대답에 더 많은 업보를 줄 수 있었으면 좋겠다! – zcaudate