SlideShare a Scribd company logo
1 of 43
7장. 매크로
아꿈사 박민욱
- 프로그래밍 기법은 대부분 언어라는 틀 안
에서 이루어진다.
- 매크로를 작성 한다는 것은 언어 자체에
무엇인가를 추가한다는 의미이다.
7.1 언제 매크로를 사용 해야 할까
매크로 클럽 규칙
첫번째 : 매크로를 사용하지 말라
두번째 : 매크로가 패턴을 추상화할 수 있는 유일한 방법일
때만 사용하라
예외 : 통일 기능을 하는 함수에 견줄 때 매크로를 작성하
는 것이 삶을 더 편하게 만든다면, 매크로를 작성해
도 좋다
7.2 제어 구조 매크로
(if (= 1 1) (println "yep, math still works
today"))
=> yep, math still works today
반대되는 unless를 만들어 봅시다
(defn unless [expr form]
(if expr nil form))
(unless false (println "this should print"))
=> this should print
(unless true (println "this should not print"))
=> this should not print
(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
첫번째 : 매크로를 실행해 나온 결과를 매크
로를 호출한 자리에 치환하여 넣는다.
이 단계를 '매크로 익스팬션 타임' 이
라고 한다.
두번째 : 일반적인 '컴파일 타임' 단계에 들어
간다.
(unless expr form)
-> (if expr nil form)
(defmacro name doc-string? attr-map? [params] body)
(defmacro unless [expr form]
(list 'if expr nil form))
(unless false (println "this should print"))
(if false nil (println "this should print"))
(unless false (println "this should print"))
| this should print
Nil
(unless true (println "this should not print"))
=> Nil
매크로의 전개
(defmacro unless [expr form]
(list 'if expr nil form))
- if 앞에 작은 따옴표를 붙임으로써, 매크로 익스팬션 타임에 if의 값
이 평가되지 않게 막는다.
- if를 평가하면 따옴표가 벗겨지고 if 자체가 컴파일 타임으로 넘겨진
다.
- expr과 form은 매크로 인자이기 때문에 따옴표를 붙이 지 않는다.
- 매크로 익스팬션 타임에 expr과 form 은 각각 그 해당 인자로 치환
된다.
- nil은 평가해도 nil이기 때문에 붙일 필요가 없다.
(macroexpand-1 form)
(macroexpand-1 '(unless flase
(println "this should print")))
=> (if false nil (println "this should print"))
(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"))
(macroexpand-1 '(.. arm getHand
getFinger))
=> (.. (. arm getHand) getFinger)
(macroexpand form)
(macroexpand '(.. arm getHand getFinger))
=> (. (. arm getHand) getFinger)
when 과 when-not
(unless false (println "this") (println "and
also this"))
=> 에러
(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")))
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)) )
(concat (list 'chain (list '. x form)) more)))
(defmacro chain
([x form] (. ${x} ${form}))
([x form & more] (chain (. ${x} ${form})
${more})))
구문 따옴표, 평가 기호, 이음 평가 기호
(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))
( defmacro chain
([x form] `(. ~x ~form))
([x form & more] `(chain (. ~x ~form) ~@more)) )
(macroexpand '(chain arm getHand getFinger))
(. (. arm getHand) getFinger)
1. 템플릿으로 다루기 위해 구문따옴표 (`)를 시작한다.
2. 각인자 앞에는 (~)를 붙여 준다3. 여러 인자를 삽입하기 위해
서는 (~@)를 사용한다.
매크로 안에서 이름 만들기
(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”}
( 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})
(let [a 1 b 2]
(bench (+ a b)))
=> {:result 3, :elapsed 39000}
(let [start 1 end 2]
(bench (+ start end)))
=> {: result 123536234634636364, :elapsed
39000}
(defmacro bench [expr]
'(let [start# (System/nanoTime)
result# ~expr]
{:result result# :elapsed (-
(System/nanoTime) start#)}))
(bench (str "a" "b"))
{:elapsed 63000, :result "ab"}
7.4 매크로의 분류
1. 매크로를 작성하지 마라
2. 매크로가 패턴을 추상화할 수 있는 유일
한 벙법일 때만 사용하라
3. 동일 기능을 하는 함수에 견줄때 매크로
를 작성하는 것이 삶을 더 편하게 만든다
면 매크로를 작성해도 좋다.
매크로 사용이 옳은 이유 매크로의 용도 예
특수 구문 조건부 평가 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
조건부 평가
(defmacro and
([] true)
([x] x)
([x & rest]
`(let [and# ~x] (1)
(if and# (and ~@rest) and#)))) (2)
(and 1 0 nil false)
-> nil
(or 1 0 nil false)
-> true
(comment & exprs)
var의 생성
클로저의 var는 def특수 구문을 이용해 만들
어 진다
defn, demacro, defmulti등은 모두 결국 def
를 호출하는 매크로다.
(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)))
(def a)
(def b)
(def c)
(def d)
(declare a b c d)
(defmacro declare
[& names] `(do ~@(map #(list 'def %)
names)))
하나씩 분석해 보자
익명함수 #(list 'def %)가 개개의 def구문을
만드는 역할을 한다.
(#(list 'def %) 'a)
-> (def a)
map은 names안의 심벌에 대해서 이 익명
함수는 적용한다.
(map #(list 'def %) '[a b c d])
-> ((def a) (def b) (def c) (def d))
결국 do 가 이 같은 표현들을 묶어서, 문법
적으로 올바른 클로저 구문이 되게 한다.
'(do ~@(map #(list 'def %) '[a b c d]))
-> (do (def a) (def b) (def c) (def d))
이 코드는 declare 매크로를 macroexpand-1
으로 전개한 결과와 동일하다.
(macroexpand-1 '(declare a b c d))
-> (do (def a) (def b) (def c) (def d))
자바와의 상호작용
Math/PI
-> 3.14159265389793
(Math/pow 10 3)
-> 1000.0
Math가 걸리적거림으로 일일이 var들을 선언 할 수
는 있다.
(def PI Math/PI)
(def pow [b e] (Math/pow b e))
(use '[clojure.contrib.import-static :only
(import-static)])
(import-static java.lang.Math PI pow)
PI
-> 3.14159265389793
(pow 10 3)
-> 1000.0
평가지연
(delay & exprs)
(def slow-clac (delay (Thread/sleep 5000) "done!"))
(force x)
slow-calc에 force를 반복해서 적용해보자
(force slow-calc)
-> "done!“ (5초후)
(force slow-calc)
-> "done!“ (바로)
구문 감싸기
( 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)
(assert expr)
논리적으로 참이 아니면 예외를 발생
(assert (= 1 1))
=> Nil
(assert (= 1 2))
=> java.lang.Exception: Assert failed: (= 1 2)
불필요한 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}

More Related Content

What's hot

14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿
유석 남
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수
유진 변
 

What's hot (20)

Boost pp 20091102_서진택
Boost pp 20091102_서진택Boost pp 20091102_서진택
Boost pp 20091102_서진택
 
Javascript 함수(function) 개념, 호출패턴, this, prototype, scope
Javascript 함수(function) 개념, 호출패턴, this, prototype, scopeJavascript 함수(function) 개념, 호출패턴, this, prototype, scope
Javascript 함수(function) 개념, 호출패턴, this, prototype, scope
 
14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿
 
Haskell study 5
Haskell study 5Haskell study 5
Haskell study 5
 
[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)
 
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
 
Haskell study 14
Haskell study 14Haskell study 14
Haskell study 14
 
Haskell study 13
Haskell study 13Haskell study 13
Haskell study 13
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 
프론트엔드스터디 E05 js closure oop
프론트엔드스터디 E05 js closure oop프론트엔드스터디 E05 js closure oop
프론트엔드스터디 E05 js closure oop
 
3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택
 
Swift 3 Programming for iOS: error handling
Swift 3 Programming for iOS: error handlingSwift 3 Programming for iOS: error handling
Swift 3 Programming for iOS: error handling
 
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
 
Javascript기초
Javascript기초Javascript기초
Javascript기초
 
프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js function프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js function
 
[새차원, 코틀린(Kotlin) 강좌] 5. Control Flow
[새차원, 코틀린(Kotlin) 강좌] 5. Control Flow[새차원, 코틀린(Kotlin) 강좌] 5. Control Flow
[새차원, 코틀린(Kotlin) 강좌] 5. Control Flow
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수
 
바다 앱 개발 실패 노하우 1부
바다 앱 개발 실패 노하우 1부바다 앱 개발 실패 노하우 1부
바다 앱 개발 실패 노하우 1부
 
Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체
 
RHive tutorial 5: RHive 튜토리얼 5 - apply 함수와 맵리듀스
RHive tutorial 5: RHive 튜토리얼 5 - apply 함수와 맵리듀스RHive tutorial 5: RHive 튜토리얼 5 - apply 함수와 맵리듀스
RHive tutorial 5: RHive 튜토리얼 5 - apply 함수와 맵리듀스
 

Viewers also liked (7)

Drawing on canvas
Drawing on canvasDrawing on canvas
Drawing on canvas
 
2.2.4 순환목록
2.2.4 순환목록2.2.4 순환목록
2.2.4 순환목록
 
Clojure in the Wild
Clojure in the WildClojure in the Wild
Clojure in the Wild
 
1.4.2 코루틴연습문제
1.4.2 코루틴연습문제1.4.2 코루틴연습문제
1.4.2 코루틴연습문제
 
클로저
클로저클로저
클로저
 
8.다중메서드
8.다중메서드8.다중메서드
8.다중메서드
 
Clojure Chapter.6
Clojure Chapter.6Clojure Chapter.6
Clojure Chapter.6
 

Similar to 7장매크로

Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기
지수 윤
 

Similar to 7장매크로 (20)

파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차
 
Macro & compilation
Macro & compilationMacro & compilation
Macro & compilation
 
Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오
 
파이썬 기본 문법
파이썬 기본 문법파이썬 기본 문법
파이썬 기본 문법
 
R 프로그래밍 기본 문법
R 프로그래밍 기본 문법R 프로그래밍 기본 문법
R 프로그래밍 기본 문법
 
일단 시작하는 코틀린
일단 시작하는 코틀린일단 시작하는 코틀린
일단 시작하는 코틀린
 
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
 
Java.next
Java.nextJava.next
Java.next
 
이산수학02
이산수학02이산수학02
이산수학02
 
Haskell study 4
Haskell study 4Haskell study 4
Haskell study 4
 
Scala
ScalaScala
Scala
 
DEVIEW-FULL-감독판.pptx
DEVIEW-FULL-감독판.pptxDEVIEW-FULL-감독판.pptx
DEVIEW-FULL-감독판.pptx
 
튜터링 #9 20120409
튜터링 #9 20120409튜터링 #9 20120409
튜터링 #9 20120409
 
R 기초 : R Basics
R 기초 : R BasicsR 기초 : R Basics
R 기초 : R Basics
 
DM_02
DM_02DM_02
DM_02
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1
 
Clojure Monad
Clojure MonadClojure Monad
Clojure Monad
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
 

7장매크로

  • 2. - 프로그래밍 기법은 대부분 언어라는 틀 안 에서 이루어진다. - 매크로를 작성 한다는 것은 언어 자체에 무엇인가를 추가한다는 의미이다.
  • 3. 7.1 언제 매크로를 사용 해야 할까 매크로 클럽 규칙 첫번째 : 매크로를 사용하지 말라 두번째 : 매크로가 패턴을 추상화할 수 있는 유일한 방법일 때만 사용하라 예외 : 통일 기능을 하는 함수에 견줄 때 매크로를 작성하 는 것이 삶을 더 편하게 만든다면, 매크로를 작성해 도 좋다
  • 4. 7.2 제어 구조 매크로 (if (= 1 1) (println "yep, math still works today")) => yep, math still works today
  • 5. 반대되는 unless를 만들어 봅시다 (defn unless [expr form] (if expr nil form))
  • 6. (unless false (println "this should print")) => this should print (unless true (println "this should not print")) => this should not print
  • 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. 첫번째 : 매크로를 실행해 나온 결과를 매크 로를 호출한 자리에 치환하여 넣는다. 이 단계를 '매크로 익스팬션 타임' 이 라고 한다. 두번째 : 일반적인 '컴파일 타임' 단계에 들어 간다.
  • 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. (unless false (println "this should print")) (if false nil (println "this should print"))
  • 11. (unless false (println "this should print")) | this should print Nil (unless true (println "this should not print")) => Nil
  • 12. 매크로의 전개 (defmacro unless [expr form] (list 'if expr nil form)) - if 앞에 작은 따옴표를 붙임으로써, 매크로 익스팬션 타임에 if의 값 이 평가되지 않게 막는다. - if를 평가하면 따옴표가 벗겨지고 if 자체가 컴파일 타임으로 넘겨진 다. - expr과 form은 매크로 인자이기 때문에 따옴표를 붙이 지 않는다. - 매크로 익스팬션 타임에 expr과 form 은 각각 그 해당 인자로 치환 된다. - nil은 평가해도 nil이기 때문에 붙일 필요가 없다.
  • 13. (macroexpand-1 form) (macroexpand-1 '(unless flase (println "this should print"))) => (if false nil (println "this should print"))
  • 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. (macroexpand-1 '(.. arm getHand getFinger)) => (.. (. arm getHand) getFinger) (macroexpand form) (macroexpand '(.. arm getHand getFinger)) => (. (. arm getHand) getFinger)
  • 16. when 과 when-not (unless false (println "this") (println "and also this")) => 에러
  • 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. 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. (concat (list 'chain (list '. x form)) more))) (defmacro chain ([x form] (. ${x} ${form})) ([x form & more] (chain (. ${x} ${form}) ${more})))
  • 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. ( 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. 매크로 안에서 이름 만들기 (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. ( 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. (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. (defmacro bench [expr] '(let [start# (System/nanoTime) result# ~expr] {:result result# :elapsed (- (System/nanoTime) start#)})) (bench (str "a" "b")) {:elapsed 63000, :result "ab"}
  • 26. 7.4 매크로의 분류 1. 매크로를 작성하지 마라 2. 매크로가 패턴을 추상화할 수 있는 유일 한 벙법일 때만 사용하라 3. 동일 기능을 하는 함수에 견줄때 매크로 를 작성하는 것이 삶을 더 편하게 만든다 면 매크로를 작성해도 좋다.
  • 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. 조건부 평가 (defmacro and ([] true) ([x] x) ([x & rest] `(let [and# ~x] (1) (if and# (and ~@rest) and#)))) (2)
  • 29. (and 1 0 nil false) -> nil (or 1 0 nil false) -> true (comment & exprs)
  • 30. var의 생성 클로저의 var는 def특수 구문을 이용해 만들 어 진다 defn, demacro, defmulti등은 모두 결국 def 를 호출하는 매크로다.
  • 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. (def a) (def b) (def c) (def d) (declare a b c d)
  • 33. (defmacro declare [& names] `(do ~@(map #(list 'def %) names)))
  • 34. 하나씩 분석해 보자 익명함수 #(list 'def %)가 개개의 def구문을 만드는 역할을 한다. (#(list 'def %) 'a) -> (def a)
  • 35. map은 names안의 심벌에 대해서 이 익명 함수는 적용한다. (map #(list 'def %) '[a b c d]) -> ((def a) (def b) (def c) (def d))
  • 36. 결국 do 가 이 같은 표현들을 묶어서, 문법 적으로 올바른 클로저 구문이 되게 한다. '(do ~@(map #(list 'def %) '[a b c d])) -> (do (def a) (def b) (def c) (def d))
  • 37. 이 코드는 declare 매크로를 macroexpand-1 으로 전개한 결과와 동일하다. (macroexpand-1 '(declare a b c d)) -> (do (def a) (def b) (def c) (def d))
  • 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. (use '[clojure.contrib.import-static :only (import-static)]) (import-static java.lang.Math PI pow) PI -> 3.14159265389793 (pow 10 3) -> 1000.0
  • 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. 구문 감싸기 ( 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. (assert expr) 논리적으로 참이 아니면 예외를 발생 (assert (= 1 1)) => Nil (assert (= 1 2)) => java.lang.Exception: Assert failed: (= 1 2)
  • 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}

Editor's Notes

  1. 가 나와야 하지만 가 나옵니다.
  2. 머가 문제인제 확인 해보면 함수의 인자가 unless에 넘겨지기도 전에 평가 된다는 것을 알수 있다.
  3. 매크로를 사용하면, 매크로에 넘겨지는 인자를 언제 평가할지 조절할 수 있다. 클로저가 매크로 호출을 만나면 두단계를 밟는다.
  4. unless를 작성하기 위해서는, 매크로 익스팬션 타임에 다음과 같이 코드 변환이 일어나도록 만들면 된다. 내부사항은 뒤에 살펴 보도록 하자
  5. 하면 내부적으로 이렇게 바뀐다.
  6. 매크로 익스팬션 타임에 어떤 일이 일어나는지 보여주는 함수다.
  7. 매크로는 매우 정교한 사고를 요하기 때문에, 머릿속으로 생각하는 것뿐 아니라 macroexpand-1을 이용해 테스트해 보는 것이 매우 중요하다.
  8. 때때로 매크로를 전개한 결과가 다른 매크로를 포함하고 있기도 한다. 이런 경우에 클로저는 코드가 매크로를 전혀 포함하지 않을 때까지 매크로를 전개한다.
  9. 이런 일을 하는것이 when과 when-not이다.
  10. do로 묶어서 한꺼번에 실행
  11. unless 쉽고 좋은 예이지만, 모든 매크로가 이처럼 간단하지는 않다. 확인 제대로 동작한다. 그러나 다음와 같은 코드는 읽기가 힘들다.
  12. 이문제를 해결하기 위해서는 템플릿 언어를 사용할수 있다 다음과 같이 chain을 다시 정의 할수 있게 된다. 빈곳만 채워 넣는 식의 접근 방법이 가능하다 동작 안됨..
  13. ~more의 템플릿에 집어 넣으면 리스트이므로 괄호를 포함하게 됩니다. 이음 평가기호를 넣어준다.
  14. 많은 매크로가 클로져의 ..과 비슷한 패턴을 따른다
  15. Let은 시작 시간을 start에 바인딩한후, 구문을 실행해 그 결과를 result에 바인딩 한다. 그리고 start로 부터 지난 시간 및 result로 이루어진 맵을 반환한다. 타임과 유사 하지만 bench라는 매크로를 만들어 보자
  16. 문제 발생 심벌 캡쳐라는 이름 충돌이 발생 전개하면 구문 따옴표 안의 심벌은 이름 공간이 명시된 이름으로 바뀌게 된다.
  17. 우리는 지역에 한정된 이름을 만들기 원하는데, 심벌 캡쳐라고 부르는 매크로 버그를 만들지 않도록 도와준다. 제대로 잘 나온다… 하지만 언제나 행운이 계속 되진 않는다. !!코드 테스트에는 이렇게 나오지 않았음 bench는 start라는 심벌에 (System/nanoTime)의 값을 바인딩하게 된다. 문제 발생 그러므로 매크로를 호출하는 쪽에서 사용할 어떤 이름과도 겹치지 않는 '유일한' 이름을 만들어 낸다면 문제는 해결된다.
  18. 구문따옴표 안에서 어떤 이름 뒤에 #을 붙이면 그 이름 뒤에 언더 스코어(_)와 고유한 ID를 붙인 새로운 심벌을 자동으로 생성해준다.
  19. 클로져와 clojure-contrib에 있는 매크로를 살펴본 결과 대부분이 매크로 클럽의 규칙을 따르고 있었다. 1. 매크로를 작성하지 마라 2. 매크로가 패턴을 추상화할 수 있는 유일한 벙법일 때만 사용하라 3. 동일 기능을 하는 함수에 견줄때 매크로를 작성하는 것이 삶을 더 편하게 만든다면 매크로를 작성해도 좋다.
  20. and는 재귀적으로 적용이 된다 인자가 없는 경우와 하나인 경우가 기저가 된다. 인자가 없는 경우 true를 반환한다. 인자가 하나 이상인 경우에는 그 인자를 반환한다. 인자가 두 개 이상 들어올 경우에는 첫 번째 인자를 조건 식으로 삼아, (1)에서 평가한다. 평가 결과가 참이면, and를 재귀적으로 불러 나머지 원소에 대한 평가를 계속 한다. (2)
  21. And는 false를 만나면 연산을 멈추고 Or은 true를 만나면 연산을 멈춘다. 이렇게 불필요한 평가를 절약하는 방식의 최고는 comment매크로다 comment 매크로는 인자를 '전혀' 평가하지 않으며, 때때로 파일의 마지막에서 API사용법을 보이는 데 이용된다.
  22. - 클로저에는 구조체를 생성하는 create-struct라는 함수가 있다. - 깔끔하지가 않다. - defstruct는 간단한 매크로로, 이미 클로저에 포함되어 있다. - 굉장히 간단하기 때문에 함수로 만들수 있겠다고 생각하겠지만 할수 없다. 왜냐하면 def는 특수 구문이기 때문이다. def는 매크로 타임에 생성 되어야 하고, 런타임에 동적으로 def를 호출할수는 없다.
  23. 이렇게 하는건 공간 낭비 임으로
  24. 클로저로 declare를 구현한 코드다
  25. 스튜어트 시에라가 이과정을 자동화할수 있는 import-static 매크로를 만들었다. import-static은 자바 클래스의 정적 멤버를 지역 이름 공간에서 간단한 이름으로 사용할 수 있도록 해준다.
  26. 4.3절 '지연 스퀀스와 무하한 시퀀스'에서 이런 매크로를 본적이 있는데 바로 lazy-seq다. 다른예로 delay가 있다. (delay에 exprs를 넘기면, 그저 exprs를 저장해 놓고 이 값을 계산하도록 강요받을 때까지 아무것도 하지 않는다. sleep을 사용해서 굉장히 긴 연산을 흉내 내는 구문을 delay에 넘겨 보자 Delay를 통해 그 평가가 지연된 구문을 샐행하려면 force를 사용한다. force를 처음 적용하면 지연된 구문을 실행한 다음 그 결과를 캐시하고 다시 force를 적용하면 캐시된 결과를 바로 반환한다. 클로져의 지연 평가를 구현하는 매크로는 모두 clojure.jar안의 자바 코드를 호출하고 있다.
  27. 구문이 실행되기 전이나 후에 특정한 행동을 추가하고 싶다면, 구문을 감싸는 매크로를 이용한다. Time은 타이머를 시작시킨뒤, 구문들을 평가하고 실행시간을 보고 한다. Let과 binding은 바인딩을 수립한 뒤, 구문들을 평가하고, 수립한 바인딩을 해제 한다. 등등 with-out-str는 임시로 *out*을 새로운 StringWriter에 다인딩한후 주어진 exprs을 평가하고 결과로 나온 문자열을 *out*으로 출력한다.
  28. 익명함수를 lamda라고 한다. 때로는 lambda를 인자로 받는 함수가 매크로를 대신할 수 있다. 익명함수를 사용할 때에는 호출하는 쪽에서 구문을 lambda로 감싸야 하기 때문에, 일이 더 많아 진다. Bench-fn이 bench의 완벽한 대채물이 아니기 때문에 매크로를 사용한다