2013-10-03 1 views
0

유창한 인터페이스로 유창한 인터페이스를 사용하여 라켓 매크로를 더 잘 배울 수있게되었습니다. 따라서이 코드를 노크하기 위해 조롱했습니다.바인드되지 않은 식별자와 # % top 구문 트랜스포머가 없습니다.

#lang racket 
(require racket/syntax) 
(struct Person (name location)) 

(define-syntax what 
    (syntax-rules (is) 
    ((what is x thing) (what is x's thing)) 

    ((what is x quote-s thing) 
    (let* ((x-str (~a (quote x))) 
      (x-id (format-id #'x-id "~a" x-str)) 
      (thing-str (~a (quote thing))) 
      (thing-proper-str (cadr (regexp-match #rx"(.*)\\?" thing-str))) 
      (thing-id (format-id #'thing-id "Person-~a" thing-proper-str))) 
     ((eval thing-id) (eval (syntax-e x-id))))))) 

(define John (Person "John Luser" "Here")) 
(what is John's name?) 

이 결과는 John: unbound identifier; also, no #%top syntax transformer is bound in: John입니다. 추측해야만한다면, 식별자와 문자열로 수행하는 작업은 기호 John의 바인딩을 제거하거나 잘못된 환경에서 평가할 것이라고 말하고 싶지만 둘 중 하나를 수정하는 방법을 모르겠습니다. 그것들의.

답변

1

와우, 바보 같은 느낌이 들어요! 단지

+0

참고 : 매크로가있는 방식으로 모든 계산이 런타임에 완료됩니다. 일반적으로 컴파일 타임에 가능한 한 많이 밀어 넣기를 원합니다. '(John의 이름은 무엇입니까?)'에서'(Person-name John)'이라는 용어를 컴파일 할 때 컴파일을 할 수 있습니다. Greg Hendershott의 Macro of Fear와 같은 튜토리얼을보고 싶을 것입니다 : http://www.greghendershott.com/fear-of-macros/ – dyoo

+0

'eval' 접근법은 또한 깨지기 쉽습니다. 반드시 당신이 잘 통제 할 수없는 환경에서의 동적 인 평가. 나는 DrRacket에서 텍스트 REPL 대 독립 실행 형 프로그램을 실행하여 결과가 달라질 것이라고 생각합니다. 또한 Racket에서 할 수있는 멋진 컴파일 타임 기능을 활용하지 않으므로 다른 옵션이 없다면'eval'을 피하십시오. – dyoo

+0

@dyoo 유감스럽게도 메타 프로그래밍은 너무 복잡하여 다른 옵션이 무엇인지 알지 못하고 어떻게 작동하는지, 어떻게 사용하는지 알지 못합니다. –

2

((eval thing-id) (eval x-id)) 그냥 비교하기 ((eval thing-id) (eval (syntax-e x-id)))을 변경하여 몇 분이 게시 한 후이를 수정 : 여기 당신이 컴파일시 단계에 떨어져 대부분의 작업을 추진하는 경우와 같은 매크로 외모 :

#lang racket 
(require (for-syntax racket/format 
        racket/syntax)) 

(struct Person (name location)) 

(define-syntax (what stx) 
    (syntax-case stx (is) 
    ((what is x thing) 
    #'(what is x's thing)) 

    ((what is x quote-s thing) 
    (let* ((thing-str (~a (syntax-e #'thing))) 
      (thing-proper-str (cadr (regexp-match #rx"(.*)\\?" thing-str))) 
      (thing-id (format-id stx "Person-~a" thing-proper-str))) 
     #`(#,thing-id x))))) 

(define John (Person "John Luser" "Here")) 
(what is John's name?) 

한 가지주의해야 할 점은 (require (for-syntax ...))은 프로그램의 소스 코드를 사전 처리하는 동안 컴파일 할 때 라이브러리를 사용하기 때문입니다.

또한 실제로는 x이 실행되고 있지 않으므로 출력 구문에 보존 된 구문을 그대로 유지할 수 있기 때문에 조금 더 간단합니다.


도움이된다면 다른 언어로 경험해보십시오. 당신이 자바에 익숙하다면 표현식을 프로그램의 어딘가에 작성하고 프로그램을 컴파일하면 어떻게되는지 생각해보십시오. Java 컴파일러가 실행될 때 "hello " 문자열, "world" 문자열 및 두 문자열 간의 문자열 연결을 생성하는 바이트 코드를 생성 할 것으로 기대합니까? 대부분의 경우 "아니오, 컴파일러는 컴파일의 일부로 리터럴 "hello world"로 다시 작성해야합니다."

이것은 전 처리입니다. Racket에 매크로를 작성하면 Racket에게 이러한 전처리 규칙을 더 잘 가르치고 있습니다.

여기서 우리는 문자열을 연결하는 방법을 알고있는 cat 명령을 추가 할 것입니다. 즉,이 연결 예제를 구현해 봅시다.

#lang racket 

(define (cat x y) 
    (string-append x y)) 

(define msg-head "hello ") 
(define msg-tail " world") 

(cat msg-head msg-tail)  
(cat "hiya " "world") 

지금, 우리는 일반 함수로, cat 모두 사용이 여기에 결국 아래로 string-append 함수 호출을 줄일 것을 알고 :

는 다음 버전의 하나입니다. 컴파일러는 cat의 두 번째 사용 방법을 만지는 방법을 알만큼 똑똑하지 않습니다.

하지만 같은 종류의 컴파일러 다시 쓰기 규칙을 수행하려는 경우 컴파일러에서 소스 코드가 cat2 개의 문자열 리터럴을 사용하려고하는 경우 컴파일러에서 컴파일하려고하면 컴파일 타임에 컴파일되지 않습니다. 바이트 코드가 더 좋습니까?

음, 그렇게하겠습니다. 버전 두 사람은 다음과 같습니다

#lang racket 

(define-syntax (cat stx) 
    (syntax-case stx() 
    [(_ x y) 
    ;; At this point, the following code is being run by the _compiler_. 
    (cond 
     ;; If in the source code we're transforming, both x and y are 
     ;; strings, we can do the concatenation at _compile time_. 
     [(and (string? (syntax-e #'x)) 
      (string? (syntax-e #'y))) 
     (let ([xy 
       (string-append (syntax-e #'x) (syntax-e #'y))]) 
      ;; Once we have this string, we still need to emit it back as the 
      ;; result of the rewrite. We want to produce a piece of _syntax_ 
      ;; in place of the original stx. 
      (datum->syntax stx xy))] 

     [else 
     ;; Otherwise, we want to produce a piece of syntax that looks like a 
     ;; plain function call to string-append. 
     (datum->syntax stx (list 'string-append #'x #'y))])])) 

(define msg-head "hello ") 
(define msg-tail " world") 
(cat msg-head msg-tail) 
(cat "hiya " "world") 

이제 변환 이런 종류의 문제가 관찰 중 하나입니다 : 우리가이 권리를 한 경우에, 아무도 그 차이를 말할 수 없을 것! : P 그렇지 않으면 깨진 변형이됩니다.차이점을보다 쉽게 ​​보려면 DrRacket 도구 모음에서 매크로 스텝을 매크로 버튼을 누릅니다. 컴파일러를 호출하지만 바이트 코드로 바뀌기 직전에 프로그램에서 어떤 변형이 발생했는지 보여줍니다.

+0

나는이 말이 합리적이라고 말할 수 있었으면 좋겠다. 그러나 인용, 인용 취소, quasiquoting 등이 어떻게 작동하는지 매우 약하다. 나는 마지막 라인까지 당신이하고있는 것을 볼 수있다. (그리고 내가 스스로해야 할 일이 많이 있었다는 것을 고려해야한다.) 그러나 컴파일 타임에 왜이 코드가 변형되었는지 그리고 나의 코드는 '# \'(#, thing-id x)'가 작동하는 방식도 정확히 알지 못한다. –

+0

http://docs.racket-lang.org/continue/#(part._.Rendering_.HTML)은 간단한 소개를한다. 정규리스트를 준 인용하기. 플랫 문자열이 아닌 구조를 다룰 수 있다는 점을 제외하고는 매우 가벼운 문자열 템플릿 언어 기능이라고 생각하십시오. 보다 포괄적 인 안내서는 http://docs.racket-lang.org/guide/qq.html을 참조하십시오. – dyoo

+0

원래 예제에서 Macro Stepper를 실행하십시오. 그 차이점을 즉시 확인해야합니다. 'syntax-rules' 대신에'syntax-case'를 사용하는 의도적 인 이유가 있습니다 : 나는 컴파일 타임에 계산을 할 수 있기를 원하며'syntax-case'는 그렇게 할 수 있습니다. 'syntax-rules'는 "왼쪽을 오른쪽으로 바꿉니다"패턴 매칭 일종의 것입니다 : 당신은'syntax-rules'로 중요한 계산을 할 수 없습니다. – dyoo

관련 문제