아직도 목 부근이 아프다. 힘도 안난다. 운동을 강하게 할 수 는 없을 것 같다. 평생 약을 먹어야 하는 것도 걱정이다. 아니 좀 짜증이라고 해야 하나...
하지만, 뭐 어쩔 수 없다고 생각하게 되었다. 내가 인자약이라 생각하고, 음식을 하나하나 제한하기로 했다.
그리고...
오랜만에 on lisp를 다시 공부하고 있다.
커먼리습을 잘 모르기에 읽는데 꽤나 힘들다. 잘 몰라도 내 시야를 넓혀주는 느낌을 준다.
이런 공부 정말 시간이 오래걸리고 내가 영어능력이 후달려서 제대로 읽고 있는지도 잘 모르지만,
꽤나 재미있다.
이제 겨우 8장을 넘겼는데, 언제 마지막까지 갈까 한숨이 나온다.
끈질기게 계속 나아가야 겠다.
; 8 When to Use Macro ; 8.1 When Nothing Else Will Do ; 일반적으로 비슷한 코드를 프로그램에서 찾는 다면 ; 서브루틴을 만들고 비슷한 일련의 코드들을 없애고 서브루틴을 호출하는 것으로 바꿀 것이다. ; 이 원칙을 리스프 프로그램에 적용하면, ; 우리는 이 "서브루틴"이 함수여야 할지 매크로여야 할 지 고민해야 한다. ; 어떤 경우에는 매크로밖에 할 수 없어서, 매크로로 결정해야 하는 경우가 있다. ; 1+ 함수 같은 경우 함수/매크로 둘다 만들 수 있다. (defun myfun-1+ (x) (+ 1 x)) (defmacro mymacro-1 (x) `(+ 1 ,x)) ; 하지만 while같ㅇ느 경우 매크로밖에 못한다. (defmacro while (test &body body) `(do () ((not ,test)) ,@body)) ; while은 body 표현식을 쪼개서(splice) do의 body부분에 넣는다. ; 게다가 평가는 test변수가 오로지 nil일 경우다. ; 함수는 이런거 못한다 함수는 호출되기 전에 매개변수가 모두 평가 호출된다. ; 이걸 알자 매크로는 함수가 못하는 4가지를 할 수 있다. ; 1. Transformation(변형) ; 커먼리습에서 setf 매크로는 평가 전에 인수를 구분(평가전에 매개변수를 다룬다)하는 매크로 클래스 중 하나다. ; 내장 액세스 함수는 종종 그 반대(역)를 가진다. 이 녀석의 목적은 내장 엑세스 함수가 조회하는 것을 설정한다(set 반대로 한다는 말) ; 예를 들어 car는 첫번째를 가져오는데 replaca는 첫번째를 설정한다. ; setf 를 함께 이용하면 이런 access function(car, cdr)를 이용하여 변수를 set할 수 있다. ; (setf (car x) 'a) 이렇게 말이다. 이 값은 (progn (rplaca x 'a) 'a)로 확장될 수 있다. ; 이 트릭을 수행하려면 setf는 첫 번째 인수를 살펴 봐야한다. 그래야 뭘로 바꿀지 알 수 있다. ; 위의 경우 replaca가 필요하다는 것을 알기 위해 setf는 첫 번째 인수가 car로 시작하는 표현식임을 확인할 수 있어야함. ; 따라서 setf 및 인수를 변환하는 다른 연산자는 매크로로 작성해야합니다. ; 그러니까 매개변수에 들어오는 표현식이 평가되지 전에 확인해야 하는 것이 있는 것이다. ; 나의 예측으로는 setf의 첫번째 매개변수의 표현식이 car이라면 replaca로 cdr이면 rplacd로 변환(transformation)할 것으로 보인다. ; 그러므로 이럴 때는 함수를 쓸 수 없다. 왜냐하면 매개변수로 들어가면 이미 평가가 되고 나서 함수에 들어오기 때문에다. ; 표현식을 볼 수 없다. ; 2. Binding(바인딩, 변수바인딩인듯) ; Lexical variables must appear directly in the source code. ; 렉시컬 변수는 소스 코드에 직접 나타나야합니다. ; 예를 들어 setq에 대한 첫 번째 인수는 평가되지 않으므로 setq에 빌드 된 것은 호출하는 함수가 아니라 setq로 확장되는 매크로 여야합니다. ; 비슷한 연산자 let을 보자, 이녀석에게 오는 매개변수들은 lambda 표현식의 매개변수로 바뀐다.(매크로를 풀면) ; do 매크로는 letdmfh ghkrwkdehlsek. ; 어떤 연산자든 매개변수의 lexical binding을 손보려면 매크로여야 한다. ; 이름이 평가되기 전에 lambda로 감싼 후 매개변수로 보내서 lexcial binding을 만들어야 한다. ; 3. Conditional evaluation.(조건에 따른 평가) ; 함수의 경우 모든 매개변수는 평가된다. ; when을 예로 들면 특정 조건에 맞을 때만 body가 평가된다. ; 이런 유연성은 매크로에서만 가능하다. ; 4. Multiple evaluation.(여러번 평가, for문 처럼) ; 함수에는 매개변수는 모두 평가될 뿐만 아니라 정확히 한.번. 만 평가된다. ; do 매크로 처럼 여러번 실행되게 하려면 매크로가 필요하다. ; 다음은 inline expansion of macros의 장점이다. ; 이제 알려줄 세 가지 장점 중 두 가지는 lexical context를 다루는 점 때문에 나타난다. ; 5. Using the calling environment. ; 매크로를 매크로를 호출하는 컨텍스트에 바인딩된 변수를 포함한 확장 코드를 생성할 수 있다. (defmacro foo (x) `(+ ,x y)) ; depends on the binding of y where foo is called. ; 이건 기본적으로 나쁜 스타일이다. ; 하지만, 환경값을 가져오기 위해 아주 드물게 필요한 기술이다. ; 6. Wrapping a new environment. ; A macro can also cause its arguments to be evaluated in a new lexical environment. ; 매크로는 또한 새로운 lexical environment에서 인수가 평가되도록 할 수 있습니다. ; let을 말하는 것! 페이지 144에 let 매크로 구현예제가 있다. ; (let ((y 2)) (+ x y)), 에서 y는 새로운 변수를 가리킨다. ; 7. Saving function calls. ; 매크로 확장의 인라인 삽입의 세 번째 결과는 컴파일 된 코드에서 매크로 호출과 관련된 오버 헤드가 없다는 것입니다. ; 런타임에서, 매크로 호출은 그 확장코드로 치환된 상태를 실행하는 것이다. ; (함수를 인라인으로 만드는 것과 비슷한 원리) ; 5,6번은 의도되지 않았을 경우(제대로 알지 못한 경우) , variable capture문제를 겪게 된다. ; 이 문제는 macro를 가장 위협하는 존재다. 이 문제는 챕터9에서 심도있게 다룰 것. ; Instead of seven ways of using macros, it might be better to say that there are six and a half. ; In an ideal world, all Common Lisp compilers would obey inline declarations, ; and saving function calls would be a task for inline functions, not macros. ; An ideal world is left as an exercise to the reader. ; 8.2 Macro or Function? ; 8.1의 내용은 쉬웠다. 대부분 연산자는 매개변수가 바로 평가되면 안된다. 매크로만 할 수 있는 건 매크로로 만들면 되는 것이었다. ; 둘다 만들어질 수 있는 경우, 우린 어떻게 선택해야 하는가? 어떤 장단점이 있는지 제대로 알아야 한다. (defun avg (&rest args) (/ (apply #'+ args) (length args))) (defmacro avg (&rest args) `(/ (+ ,@args) ,(length args))) ; 여기서 함수버전은 각 avg호출에 length가 불필요하게 실행됨. ; 컴파일 타임에 우리는 인수의 값을 알지 못할 수도 있지만 얼마나 많은지 알기 때문에 'length'에 대한 호출도 가능합니다. ; 뭔 말이야, 몇개인지 어떻게 안다는 거지... ; 뭐 여튼 컴파일 타임에 인수의 갯수를 알 수있으니 length호출을 매번 할 필요는 없다는 말인가? ; 여기서 함수를 쓸지 매크로를 쓸지 선택을 도와줄 요소들이 있다. ; 1. Computation at compile-time(컴파일타임에 계산이 된다.) ; 매크로 호출에서 계산은 두번 일어난다 : 매크로가 확장 될 때와 확장이 평가 될 때. ; 리습에서 모든 매크로 확장은 프로그램이 컴파일 될 때 일어나며, 모든 ; 컴파일 타임에 계산이 완료된 비트들 중 1비트도 프로그램이 실행될 때 속도를 늦추지 않을 것이다. ; If an operator could be written to do some of its work in the macroexpansion stage, it will be more efficient to make it a macro ; 만약 한 연산자가 매크로 확장 단계에서 일분 작업을 미리 수행할 수 있게 할 수 있다면, 매크로로 만들어 더 효율적이게 만들 수 있는것이다. ; 왜냐하면 어떤 아무리 똑똑한 컴파일러가 와도 스스로 할 수 없다. 함수는 런타임에 수행되어야 하기 때문이다. ; Chapter 13에서 avg 매크로가 어떻게 확장하는지 보여준다. ; 2. Integration with Lisp(Lisp와의 통합) ; 때때로 함수 대신 매크로를 사용하면 프로그램이 Lisp와보다 밀접하게 통합 될 수 있다. ; 특정 문제를 해결하기 위해 프로그램을 작성하는 대신! 매크로를 사용하여 Lisp가 이미 해결 방법을 알고있는 문제로 변환 할 수 있다. ; 이 방법은, 가능하면, 일반적으로 프로그램을 더 작고 효율적으로 만든다. ; 더 작아지고, 리스프가 우리 일부 작업을 수행해주기 때문에, 더 효율적으로, 프로덕션 리스프 시스템은 일반적으로 유저 프로그램보다 fat sweated(땀투성이가 된다?)하기때문에 ; (아마 리스프 시스템이 유저가 만드는 프로그램보다 더 커지기 때문에? 더 일을 많이 하기 때문에, 유저는 프로그램을 만들 때 효율적으로 만든다는 건가) ; 이런 장점은 대부분 embedded languages에서 나타난다. 19챕터에서 보여줄 것임. ; 3. Saving function calls.(함수호출을 줄인다) ; 매크로 호출은 호출이 되는 그곳에 바로 확장된 내용이 코드 안으로 들어간다(치환된다). ; 따라서 자주 사용하는 코드를 매크로로 작성하면 사용될 때마다 함수 호출을 절약할 수 있다. ; Lisp의 초기 방언에서 리습 프로그래머는 이 매크로 속성을 활용하여 런타임에 함수 호출을 절약했다. ; 일반적인 Lisp에서 이 작업은 인라인으로 선언 된 함수에 의해 수행됩니다. (inline으로 함수를 만든다는 듯, 이름이 없음) ; 함수를 [인라인]으로 선언하면 매크로와 마찬가지로 호출 코드로 바로 컴파일되도록 요청합니다. ; 그러나 여기에는 이론과 실제 사이에 차이가 있다. ; CLTL2 (p.229)는 "컴파일러는 이 선언을 무시해도됩니다"라고 말하고 일부 Common Lisp 컴파일러는 그렇게합니다. ; 이러한 컴파일러를 사용해야하는 경우 매크로를 사용하여 함수 호출을 절약하는 것이 여전히 합리적 일 수 있습니다.(인라인을 무시하는 컴파일러) ; 어떤 경우에는 효율성과 Lisp와의 통합이 매크로 사용에 대한 강력한 논거가 될 수 있다. ; 19 장의 쿼리 컴파일러에서 보면, 런타임에 계산되어야할 녀석들이 컴파일 타임에서 계산이 되니까(또 그 양이 많아서), ; 전체 프로그램을 하나의 거대한 매크로로 전환하는 것을 정당화 할 수 있다. ; 이렇게 전체가 거대한 매크로가 되는 전환은 속도를 빠르게 하기도 하지만 프로그램을 리스프에 더 가깝게 하기도 한다. (domain specific language가 되는듯) ; 이렇게 바뀐 후에는 쿼리 내에서 Lisp 표현식 (예 : 산술 표현식)을 사용하는 것이 더 쉽다. ; THE CONS 단점 ; 4. Functions are data, while macros are more like instructions to the compiler. ; 함수는 데이터지만 매크로는 컴파일러에게 보내는 명령어들과 비슷하다. ; 함수는 리턴하거나 매개변수를 받거나 자료구조 안에 저장될 수 있지만, 매크로는 아무것도 못한다. ; 람다표현식 안에다가 매크로를 넣어서 이런 것들을 할 수 있도록 트릭을 쓸 수도 있겠다. ; 예를들어 apply,funcall같은 것을 매크로에 적용해야 한다면 (funcall #'(lambda (x y) (avg x y)) 1 3) 2 ; 하지만 편하지는 않다. 그리고 이게 항상 되는 것은 아니다. ; avg같이 단순한 매크로라 하더라도 말이다. 매크로는 &rest 매개변수를 가진다. ; 이 안에 다양한 인수를 전달할 방법이 없습니다. 그렇다 람다에서 받는다하더라도 그걸 풀어서 넣는 방법이 있을까. ; 그 매개변수 자체를 데이터로 조작해야 하는데 그건 매크로에서 할 수 있을 것이다. ; 5. Clarity of source code (소스코드의 명확성, 코드가 더러워짐) ; 매크로는 함수로 만드는 것보다 읽기가 힘들다. 그러므로 매크로를 쓴다는 것은 눈에 띌만큼 낫다는 것을 알고 있을 때다. ; 6. Clarity at runtime. (런타임에서의 명확성, 런타임에서 디버깅이 힘들다) ; 매크로는 종종(항상) 함수보다 디버깅이 힘들다. ; 매크로는 확장시 사라지므로 런타임에서는 보이는 녀석으로는 무엇을 하는 녀석인지 제대로 설명되지 못한다. ; 전혀 작동하지 않으면 trace는 매크로 호출 자체가 아니라 매크로의 확장 기능 호출을 보여줍니다. ; 7. Recursion (재귀가 문제) ; 매크로에서 재귀를 사용하는 것은 함수랑 다르다. (힘들다) ; 매크로에서 확장된 함수은 재귀적일 수 있지만 확장하는 것 자채는 쉽지 않다. ; 섹션 10.4는 매크로의 재귀를 다룰 것이다. ; 매크로 사용 결정은 위 고려사항들을 균형있게 다뤄야 한다. ; 오로지 경험만이 무엇이 우선으로 되는지 알게 된다. ; 그러나 다음 장에 나오는 매크로의 예는 매크로가 유용한 대부분의 상황을 다룬다. ; 미래에 만들어진 매크로가 아래 주어진 매크로와 유사하다면, 작성하는 것이 안전 할 것이다. ; 마지막으로 런타임시의 명확성(단점 6번)은 거의 문제가 되지 않다. ; 많은 매크로를 사용하는 코드의 디버깅은 예상만큼 어렵지 않다. ; 매크로 정의의 길이가 수백 줄인 경우 런타임에 확장을 디버그하는 것이 불쾌 할 수 있습니다. ; 그러나 유틸리티는 최소한 작고 신뢰할 수있는 계층으로 작성되는 경향이 있다. ; 일반적으로 그 정의는 15줄 미만이다. 이런 매크로에서는 보는게 어렵진 않을 거다. ; 8.3 Applications for Macros (매크로 응용) ; 어떤 종류의 응용 프로그램에서 사용할 수 있을까? ; 가장 가까운 것(잘 맞는 것)은 syntactic transformation이다. ; 19-24 장의 모든 syntactic transformation(구문변환)으로 설명할 수 있는 전체 프로그램을 제시한다. ; 이런 것들은 실제로 모두 매크로다. ; 매크로앱은 작은 형태의 범용 매크로(while 매크로)와 큰 특정목적에 맞는 매크로 사이의 연속이다. ; 한쪽 끝에는 [유틸리티]가 있으며, 모든 Lisp에 내장 된 것과 유사하다. ; 그것들은 일반적으로 작고 일반적이며 독립적으로 작성된다. ; 그러나 특정 클래스의 프로그램에 대한 유틸리티도 작성할 수 있다. 이제 그래픽 프로그램에 사용할 매크로 모음이 있으면 그래픽을 위한 프로그래밍 언어처럼 보이기 시작할 것이다. ; 이 연속의 맨 끝에서(유틸리티의 반대편), 매크로를 사용하면 Lisp와 다른 언어(매크로로 만들어진 새로운 언어)로 전체 프로그램을 작성할 수 있습니다. ; 이러한 방식으로 사용되는 매크로는 embedded languages를 구현한다고합니다. ; 유틸리티 매크로는 bottom-up 스타일의 첫번째 자손이다. ; 심지어 프로그램이 너무 작아서 계층을 쌓기어렵다해도, 가장 낮은 계층에 Lisp자체를 추가하면 도움이 될 것이다. ; 유틸리티 nil!을 보자, 이녀석의 매개변수들을 모두 nil로 값을 넣는다. 이런 건 매크로로만 가능하다. (defmacro nil! (x) `(setf ,x nil)) ; nil!을 보자, 어떤 사람은 이걸 보고 이 매크로는 아무것도 하지 않는 녀석이라고 말 할 것이다. 단지 타이핑을 절약할 뿐이라는 것이라고. ; 맞다. 하지만 모든 매크로가 하는 일은 타이핑 절약이다. ; 컴파일러의 일은 기계어로 타이핑을 절약하는 일이다. ; 유틸리티의 가치를 과소평가 하면 안된다, 왜냐하면 이것들의 효과는 누적된다. ; 간단한 매크로의 몇몇 계층이 우아한 프로그램과 이해할 수 없는 프로그램의 차이를 만든다. ; 대부분 유틸리티는 이미 당신이 구현한 패턴들에서 나온다. ; 당신의 코드에서 패턴이 보이면, 유틸리티로 바꾸는 것을 고려하자. ; 패턴이 있는 일은 컴퓨터가 잘하는 거다. 왜 프로그램이 당신을 위해 해줄 수 있는데, 왜 그 프로그램에서 주어진 것들로만 개발하여 당신을 괴롭히는가. ; 어떤 프로그램을 작성할 때, 같은 일반적인 형태의 여러 다른 장소에서 [do] 루프를 사용한다고 가정하자. (do () ((not)) ) ; 이런 패턴을 반복적으로 봤다면, 이 패턴은 종종 제대로 딱 맞는 이름이 있을 것이다. ; 여기서 그 이름은 while이라고 부른다. 이걸 새로운 유틸리티로 제공하고 싶다면, 매크로를 써야 한다. ; 왜냐하면! 우리는 조건적으로 평가를 해야 하고, 반복적으로 평가해야 한다. ; while의 구현은 page 91에 있다. (defmacro while (test &body body) `(do () ((not ,test)) ,@body)) 이제 이렇게 쓰면 된다. (while ) ; 코드가 더 짧아지고 더 명확하게 선언된다. ; 인수를 변환하는 기능은 인터페이스 작성에서 매크로가 유용하게 되는 이유다. ; 적절한 매크로를 사용하면 길고 복잡한 표현이 필요한 경우! 짧고 간단한 표현으로 바꿀 수 있다. ; 그래픽 인터페이스를 예로들면 비록 그래픽 인터페이스에서 앤드유저를 위한 매크로 작성의 필요성은 감소하지만 ; 프로그래머는 그 어느 때 보다 많이 사용한다. ; 가장 일반적인 예는 defun이다. 이녀석 때문에 함수를 바인딩하는 것이 표면적으로 Pascal/C 같은 언어와 유사하게 만들 수 있게 된 것이다. ; Chapter 2에서 보여줬지만 아래 두 표현식은 같은 효과를 낸다. (defun foo (x) (* x 2)) (setf (symbol-function 'foo) #'(lambda (x) (* x 2))) defun매크로는 후자의 표현식으로 바꿔주는 것으로 구현된다(대략적으로) (defmacro our-defun (name params &body body) `(progn (setf (symbol-function ',name) #'(lambda ,params (block ,name ,@body))) 'name)) ; while,nil!같은 매크로는 general-purpose 유틸리티로 볼 수 있다. 이것들은 모든 리습 프로그램에서 사용할 수 있다. ; 하지만! 특정 도메인은 그들만의 유틸리티를 가질 수 있다. ; base lisp(리스프언어)만이 우리가 확장해야할 계층이라고 생각하면 안된다. ; CAD 프로그램을 예로들자, 가장 좋은 결과는 때때로 두개의 계층으로 작성하는 것이다. ; 1. a language(좀 더 마일드하게 표현하면 툴킷), CAD program만을 위한 언어를 만드는 것이다. ; 2. 그리고 그 계층 위에 특정 앱을 구현하는 것이다. ; lisp는 다른 언어가 당연스럽게 여기는 구분을 흐릿하게 만든다. ; 다른 언어에서는, 컴파일타임/런타임, 프로그램/데이터, 언어/프로그램 같은 것들 사이에 개념적 차이가 있다. ; Lisp에서는 오직 대화로 만들어지는 규칙(conversational conventions)에선 존재한다.(너희들이 정하는 거다? 뭐 이런 뜻이려나) ; 뚜렷한 구분선이 없다. 예를들어보자, 언어와 프로그램의 구분선이 리습에 확실하게 있는지... ; 당신은 문제가 있는 곳바다 선을 그릴 수 있다. 따라서 기본 코드 레이어(기본 코드 계층에서 만든 매크로 포함)를 툴킷이라 할지, 언어라 할지 그건 용어의 차이에 불과하다. ; 이걸 언어라고 생각할 때의 장점은, Lisp를 사용하는 것 같이, 유틸리티를 사용하여 언어를 확장할 수 있다는 것이다. ; 대화식 2D 그리기 프로그램을 만든다 가정하자. ; 간단히 하기 위해, 프로그램에 처리되는 유일한 객체는 원점 와 벡터 로 표시되는 선분(line segments)이라 가정하자. ; 이런 프로그램이 해야 할 일은 하나의 개체 그룹을 미끄러지듯이 움직이게 하는 것이다.(slide) ; 이것에 그림 8.1의 move-objs가 하는 일이다. 효율적으로, 우리는 각 연산이 완료되엇을 때 스크린의 전부를 redraw하고 싶지 않다. ; 우리는 변경된 부분만 redraw하고 싶다. 하여, bounds함수를 두번(이동전,후) 호출하여 (min x, min y, max x, max y 사각형)객체 그룹의 경계를 리턴한다. ; "move-objs"의 작동 부분은 "bounds"에 대한 두 호출 사이에 끼워져 이동 전후에 경계 사각형을 찾은 다음 영향을받는 전체 영역을 다시 그린다. ; "scale-objs"는 객체의 그룹의 사이즈를 바꾼다. 역시 비슷한 패턴을 가진다. 이건 뭐 회전, 반전, 뭐 이런거에 전부 적용될 듯하다. ; 여기서 그림 8.1의 구현을 보자. ; Fig 8.1 Original move and scale. (defun move-objs (objs dx dy) (multiple-value-bind (x0 y0 x1 y1) (bounds objs) (dolist (o objs) (incf (obj-x o) dx) (incf (obj-y o) dy)) (multiple-value-bind (xa ya xb yb) (bounds objs) (redraw (min x0 xa) (min y0 ya) (max x1 xb) (max y1 yb))))) (defun scale-objs (objs factor) (multiple-value-bind (x0 y0 x1 y1) (bounds objs) (dolist (o objs) (setf (obj-dx o) (* (obj-dx o) factor) (obj-dy o) (* (obj-dy o) factor))) (multiple-value-bind (xa ya xb yb) (bounds objs) (redraw (min x0 xa) (min y0 ya) (max x1 xb) (max y1 yb))))) ; 매크로를 이용하면 우리는 이 패턴의 코드를 추상화할 수 있다. ; 그림 8.2 with-redraw는 이 패턴의 스켈레톤을 제공한다. ; 이렇게 하니 함수들이 4줄로 바뀌게 된다. 각각! ; Fig 8.2 Move and scale filleted (defmacro with-redraw ((var objs) &body body) (let ((gob (gensym)) (x0 (gensym)) (y0 (gensym)) (x1 (gensym)) (y1 (gensym))) `(let ((,gob ,objs)) (multiple-value-bind (,x0 ,y0 ,x1 ,y1) (bounds ,gob) (dolist (,var ,gob) ,@body) (multiple-value-bind (xa ya xb yb) (bounds ,gob) (redraw (min ,x0 xa) (min ,y0 ya) (max ,x1 xb) (max ,y1 yb))))))) (defun move-objs (objs dx dy) (with-redraw (o objs) (incf (obj-x o) (dx) (incf (obj-y o) (dy))) (defun scale-objs (objs factor) (with-redraw (o objs) (setf (obj-dx o) (* (obj-dx o) factor) (obj-dy o) (* (obj-dy o) factor)))) ; 우리는 이 with-redraw를 대화식 그리기 프로그램을 위한 언어 안에 있는 구성물로 볼 수 있다.(언어의 파트가 된 것?) ; 우리가 더 많은 매크로를 개발함에 따라, 그것들은 실제로 이름뿐만 아니라 프로그래밍 언어와 유사하게 될 것이며, ; 우리의 응용 프로그램 자체는 특정 요구에 맞게 정의 된 언어로 작성된 프로그램에서 기대할 수있는 우아함을 보이기 시작할 것. ; 매크로의 다른 주요용도는 embedded language를 만드는 것이다. ; 리습은 프로그래밍 언어를 작성하기 아주 좋은 언어이다. ; 왜냐하면 리습 프로그램은 리스트로 표현될 수 있으며, 리습은 그렇게 표현된(리스트로 표현된?!) 빌트인 파서(read)와 컴파일러(compile)가 있다. ; 변환을 수행하는 코드를 컴파일하여 내장 언어 컴파일러를 암시적으로 가질 수 있다. (25 페이지). ; embedded language는 리스프 위에 작성되지 않은 언어이다. 하여 신텍스는 리스프와 새로운 언어에 특정한 구조들의 혼합이다. ; 임베디드 언어를 구현하는 나이브한 방법은 리습에서 인터프리터를 작성하는 것이다. ; 가능한 경우, 더 나은 방법은 변환(transformation)을 통해 언어를 구현하는 것이다. ; 인터프리터가 해당 표현식을 평가하기 위해 실행했을 리습 코드로 변환하라. ; 이 개념에서 매크로가 들어오는 것이다. ; 매크로의 역할은 한 표현식의 형태를 다른 형태로 변환하는 것이다. 하여 임배디드언어(내장언어)를 만들 때 자연적으로 선택하게 되는 기술이다. ; 일반적으로 변환으로 임베디드 언어를 더 많이 구현할 수록 좋다. ; 첫번째, 일을 적게한다. ; 예를 들어, 새로운 언어에 산술이 있다면 숫자 수량을 표현하고 조작하는 모든 복잡성에 직면 할 필요는 없습니다. ; 만약 리습의 산술능력이 당신의 목적에 충분한 만큼 괜찮다면, 산술 표현식을 동등한 Lisp 표현식으로 변환하고 나머지는 Lisp에 남겨 둘 수 있습니다. ; 변환을 사용하면 일반적으로 내장 언어도 더 빨라진다. ; 인터프리터는 속도와 관련하여 고유한 단점이 있다. ; 예를 들어 루프 내에서 코드가 발생하면 인터프리터는 각 반복에 대해 작업을 수행해야하는 경우가 종종 있습니다. 컴파일 된 코드에서는 한 번만 수행 할 수 있는데... ; 자체 인터프리터가 있는 임베디드 언어는 느려진다. 인터프리터 자체가 컴파일된다해도 그렇다. ; 그러나 새로운 언어의 표현식이 리습으로 변환되면, 결과코드는 리습컴파일러에 의해 컴파일 될 수 있다. ; 이런게 구현된 언어는 런타임에 인터프리테이션의 오버헤드를 겪지 않아도 된다. ; 언어에 맞는 진정한 컴파일러 작성은 매크로가 최상의 성능을 제공한다. ; 실제로, 새로운 언어를 변형시키는 매크로는 이것을 위한 컴파일러라고 볼 수 있다. ; 대부분의 작업을 수행하기 위해 기존 lisp 컴파일러에 의존하는 것이다. ; 여기서 임베디드 언어에 대한 예제는 다루지 않을 것이며 19-25에서 다룰 것임 ; 챕터 19는 인터프리팅과 임베디드 언어 변환과의 차이를 다룰 것이다. ; 그리고 두 방식에 대한 구현을 각각 제시한다. ; 어떤 커먼리습을 책은 매크로의 스코프가 아주 제한적이라고 한다. ; 그리고 CLTL1에 정의된 연산자의 사실을 증거로 인용하는데, 10% 미만의 매크로만이 구현되어 있다고 한다. ; 이 말은 집이 벽돌로 되어 있다고 가구도 벽돌로 되어있어야 한다는 논리다. ; 커먼리습 프로그램 안에 매크로 비율은 전적으로 해야 할 일이 달려 있습니다. ; 일부 프로그램에는 매크로를 아예 가지지 않는다. ; 일부 프로그램은 모두 매크로 일 수 있다.