Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

7장매크로

1,416 views

Published on

  • Be the first to comment

7장매크로

  1. 1. 7장. 매크로 아꿈사 박민욱
  2. 2. - 프로그래밍 기법은 대부분 언어라는 틀 안 에서 이루어진다. - 매크로를 작성 한다는 것은 언어 자체에 무엇인가를 추가한다는 의미이다.
  3. 3. 7.1 언제 매크로를 사용 해야 할까 매크로 클럽 규칙 첫번째 : 매크로를 사용하지 말라 두번째 : 매크로가 패턴을 추상화할 수 있는 유일한 방법일 때만 사용하라 예외 : 통일 기능을 하는 함수에 견줄 때 매크로를 작성하 는 것이 삶을 더 편하게 만든다면, 매크로를 작성해 도 좋다
  4. 4. 7.2 제어 구조 매크로 (if (= 1 1) (println "yep, math still works today")) => yep, math still works today
  5. 5. 반대되는 unless를 만들어 봅시다 (defn unless [expr form] (if expr nil form))
  6. 6. (unless false (println "this should print")) => this should print (unless true (println "this should not print")) => this should not print
  7. 7. (defn unless [expr form] (println "about to test...") (if expr nil form)) (unless false (println "this sould print")) => this should print => about to test (unless true (println "this sould not print")) => this should not print => about to test
  8. 8. 첫번째 : 매크로를 실행해 나온 결과를 매크 로를 호출한 자리에 치환하여 넣는다. 이 단계를 '매크로 익스팬션 타임' 이 라고 한다. 두번째 : 일반적인 '컴파일 타임' 단계에 들어 간다.
  9. 9. (unless expr form) -> (if expr nil form) (defmacro name doc-string? attr-map? [params] body) (defmacro unless [expr form] (list 'if expr nil form))
  10. 10. (unless false (println "this should print")) (if false nil (println "this should print"))
  11. 11. (unless false (println "this should print")) | this should print Nil (unless true (println "this should not print")) => Nil
  12. 12. 매크로의 전개 (defmacro unless [expr form] (list 'if expr nil form)) - if 앞에 작은 따옴표를 붙임으로써, 매크로 익스팬션 타임에 if의 값 이 평가되지 않게 막는다. - if를 평가하면 따옴표가 벗겨지고 if 자체가 컴파일 타임으로 넘겨진 다. - expr과 form은 매크로 인자이기 때문에 따옴표를 붙이 지 않는다. - 매크로 익스팬션 타임에 expr과 form 은 각각 그 해당 인자로 치환 된다. - nil은 평가해도 nil이기 때문에 붙일 필요가 없다.
  13. 13. (macroexpand-1 form) (macroexpand-1 '(unless flase (println "this should print"))) => (if false nil (println "this should print"))
  14. 14. (defmacro bad-unless [expr form] (list 'if 'expr nil form)) (macroexpand-1 '(bad-unless false (println "this should print"))) => (if expr nil (println "this should print"))
  15. 15. (macroexpand-1 '(.. arm getHand getFinger)) => (.. (. arm getHand) getFinger) (macroexpand form) (macroexpand '(.. arm getHand getFinger)) => (. (. arm getHand) getFinger)
  16. 16. when 과 when-not (unless false (println "this") (println "and also this")) => 에러
  17. 17. (when test & body) (when-not test & body) (defmacro when-not [test & body] (list 'if test nil (cons 'do body))) (macroexpand-1 '(when-not false (print "1“)(print "2)))) => (if false nil (do (print "1") (print "2")))
  18. 18. 7.3 더 쉽게 매크로 작성하기 매크로 호출 전개 (chain arm getHand) (. arm getHand) (chain arm getHand, getFinger) (. (. arm getHand) getFinger) ( defmacro chain [x form] (list '. x form) ) chain은 인자가 몇개건 모두 받을 수 있어야 한다. (재귀적 정의를 사용) ( defmacro chain ([x form] (list '. x form)) ([x form & more] (concat (list 'chain (list '. x form)) more)) )
  19. 19. (concat (list 'chain (list '. x form)) more))) (defmacro chain ([x form] (. ${x} ${form})) ([x form & more] (chain (. ${x} ${form}) ${more})))
  20. 20. 구문 따옴표, 평가 기호, 이음 평가 기호 (defmacro chain [x form] `(. ~x ~form)) (macroexpand '(chain arm getHand)) => (. arm getHand) ( defmacro chain ([x form] `(. ~x ~form)) ([x form & more] `(chain (. ~x ~form) ~more)) ) (macroexpand '(chain arm getHand getFinger)) => (. (. arm getHand) (getFinger))
  21. 21. ( defmacro chain ([x form] `(. ~x ~form)) ([x form & more] `(chain (. ~x ~form) ~@more)) ) (macroexpand '(chain arm getHand getFinger)) (. (. arm getHand) getFinger) 1. 템플릿으로 다루기 위해 구문따옴표 (`)를 시작한다. 2. 각인자 앞에는 (~)를 붙여 준다3. 여러 인자를 삽입하기 위해 서는 (~@)를 사용한다.
  22. 22. 매크로 안에서 이름 만들기 (time (str "a" "b")) =>"Elapsed time: 0.06 msecs“ (let [start (System/nanoTime) result (str "a" "b")] {:result result :elapsed (- (System/nanoTime) start)}) => {:elapsed 61000, :result “ab”}
  23. 23. ( defmacro bench [expr] `(let [start (System/nanoTime) result ~expr] {:result result :elapsed (- (system/nanoTime) start)}) ) (bench (str "a" "b")) Err (macroexpand-1 '(bench (str "a" "b")))  (clojure.core/let [examples.macros/start (System/nanoTime) Examples.macros/result (str “a” “b”)] {:elapsed (clojure.core/- (System/nanoTime) examples.macros/start) :result examples.macros/result})
  24. 24. (let [a 1 b 2] (bench (+ a b))) => {:result 3, :elapsed 39000} (let [start 1 end 2] (bench (+ start end))) => {: result 123536234634636364, :elapsed 39000}
  25. 25. (defmacro bench [expr] '(let [start# (System/nanoTime) result# ~expr] {:result result# :elapsed (- (System/nanoTime) start#)})) (bench (str "a" "b")) {:elapsed 63000, :result "ab"}
  26. 26. 7.4 매크로의 분류 1. 매크로를 작성하지 마라 2. 매크로가 패턴을 추상화할 수 있는 유일 한 벙법일 때만 사용하라 3. 동일 기능을 하는 함수에 견줄때 매크로 를 작성하는 것이 삶을 더 편하게 만든다 면 매크로를 작성해도 좋다.
  27. 27. 매크로 사용이 옳은 이유 매크로의 용도 예 특수 구문 조건부 평가 when, when-not, and, or, comment 특수 구문 var를 생성하기 위해 defn, defmacro, defmulti, defstruct, declare 특수 구문 자바와 상호작용하기 위해 .., doto, import-static 호출 시 편의 평가를 지연하기 위해 Lazy-cat, lazy-seq, delay 호출 시 편의 구문들을 감싸기 위해 With-open, dosync, with-out-str, time, assert 호출 시 편의 불필요한 lambda를 피하기 위해 With-open, dosync, with-out-sr, time, assert
  28. 28. 조건부 평가 (defmacro and ([] true) ([x] x) ([x & rest] `(let [and# ~x] (1) (if and# (and ~@rest) and#)))) (2)
  29. 29. (and 1 0 nil false) -> nil (or 1 0 nil false) -> true (comment & exprs)
  30. 30. var의 생성 클로저의 var는 def특수 구문을 이용해 만들 어 진다 defn, demacro, defmulti등은 모두 결국 def 를 호출하는 매크로다.
  31. 31. (create-struct & key-symbols) (def person (create-struct :first-name :last- name)) (defstruct name & key-symbols) (defmacro defstruct [name & keys] `(def ~name (create-struct ~@keys)))
  32. 32. (def a) (def b) (def c) (def d) (declare a b c d)
  33. 33. (defmacro declare [& names] `(do ~@(map #(list 'def %) names)))
  34. 34. 하나씩 분석해 보자 익명함수 #(list 'def %)가 개개의 def구문을 만드는 역할을 한다. (#(list 'def %) 'a) -> (def a)
  35. 35. map은 names안의 심벌에 대해서 이 익명 함수는 적용한다. (map #(list 'def %) '[a b c d]) -> ((def a) (def b) (def c) (def d))
  36. 36. 결국 do 가 이 같은 표현들을 묶어서, 문법 적으로 올바른 클로저 구문이 되게 한다. '(do ~@(map #(list 'def %) '[a b c d])) -> (do (def a) (def b) (def c) (def d))
  37. 37. 이 코드는 declare 매크로를 macroexpand-1 으로 전개한 결과와 동일하다. (macroexpand-1 '(declare a b c d)) -> (do (def a) (def b) (def c) (def d))
  38. 38. 자바와의 상호작용 Math/PI -> 3.14159265389793 (Math/pow 10 3) -> 1000.0 Math가 걸리적거림으로 일일이 var들을 선언 할 수 는 있다. (def PI Math/PI) (def pow [b e] (Math/pow b e))
  39. 39. (use '[clojure.contrib.import-static :only (import-static)]) (import-static java.lang.Math PI pow) PI -> 3.14159265389793 (pow 10 3) -> 1000.0
  40. 40. 평가지연 (delay & exprs) (def slow-clac (delay (Thread/sleep 5000) "done!")) (force x) slow-calc에 force를 반복해서 적용해보자 (force slow-calc) -> "done!“ (5초후) (force slow-calc) -> "done!“ (바로)
  41. 41. 구문 감싸기 ( with-out-str & exprs ) (with-out-str (print "hello, ") (print "world")) -> "Hello, World" (defmacro with-out-str [& body] `(let [s# (new java.io.stringWriter)] (1) (binding [*out* s#] (1) ~@body (2) (str s#)))) (3) 1. 설정 : 구문을 평가하기 앞서서 특정한 환경을 만들어 낸다. let이나 binding을 통해 새로운 바인딩을 만든다. (1) 2. 평가 : 인자로 받은 구문을 평가한다. 평가 해야할 구문이 보통 여러 개이기 때문에, 이름 평 가 기호(~@)를 붙이게 된다. (2) 3. 해제 : 생성 햇던 환경을 원래대로 되돌리고, 적절한 값을 반환한다. (3)
  42. 42. (assert expr) 논리적으로 참이 아니면 예외를 발생 (assert (= 1 1)) => Nil (assert (= 1 2)) => java.lang.Exception: Assert failed: (= 1 2)
  43. 43. 불필요한 lambda피하기 7.3 절 bench (defn bench-fn [f] (let [start (system/nanoTime) result (f)] {:result result :elapsed (- (System/nanoTime) start)})) (bench (+ 1 2)) => {:elapsed 44000, :result 3} (bench-fn (fn [] (+ 1 2))) => {:elasped 53000, :result 3}

×