2011-11-30 7 views
5

Próbuję napisać makro w lisp, które zwraca nth wyrażenie przekazane do niego i tylko ocenia to wyrażenie. Na przykład:Makra Lispa oceniające wyrażenia, gdy nie chcę tego do

(let ((n 2)) 
    (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))) 

powinien wrócić 3. Otrzymuję podział przez błąd 0. Moja definicja makr jest następująca:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ,expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

Czy mam pojęcie, co robię źle? Dzięki.

EDIT:

Dzięki @Vsevolod Dyomkin za pomoc z powyższej strony. Teraz jest jeszcze jeden problem. Gdy próbuję wykonać

(let ((n 3) (x "win") (y "lose")) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

Otrzymuję komunikat o błędzie Error: Attempt to take the value of the unbound variable 'Y'.

mój zaktualizowany kod wygląda następująco:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym)) 
     (num (gensym))) 
    `(let ((,num (eval ,n))) 
     (do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
      ((= ,i ,num) (eval (car ,expr))))))) 
+1

Dlaczego wywołujesz 'eval' w makrze? – leppie

+0

@pelpie, ponieważ jeśli nie, zwraca (princ y), ale chcę, aby to było oceniane. – Daniel

+1

"Y" nie istnieje na tym etapie. Co próbujesz zrobić? – leppie

Odpowiedz

1

Musicie qoute,expressions coś takiego:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

W przeciwnym razie, co dostaniesz jest to - jest lista expressions wstawiany jako:

CL-USER> (let ((n 2)) 
      (macroexpand-1 '(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))) 
(DO ((#:G864 1 (1+ #:G864)) 
    (#:G865 ((/ 1 0) (+ 1 2) (/ 1 0)) (CDR #:G865))) 
    ((= #:G864 N) (CAR #:G865))) 
+0

Ach, to jest pomocne. Wtedy wyobrażam sobie, że będę musiał wrzucić 'eval', aby faktycznie ocenić' (+ 1 2) 'zamiast tylko zwrócić' '(+ 1 2) ' – Daniel

+0

Poprosiłem o powyższe pytanie. Czy myślisz, że możesz spróbować z tym pomóc? Dzięki. – Daniel

+0

Powiedziałbym, że najłatwiejszym sposobem osiągnięcia celu, tj. Oceny tylko n-tego wyrażenia, byłoby coś takiego: '(defmacro nth-expr (n & body wyrażeń) (n-ty wyrażeń)) –

5

Najważniejsze jest, aby FIRST come wraz z rozszerzeniem.

Jak powinien wyglądać działający kod? Kod, do którego zostanie rozszerzone użycie makra?

Następnie należy napisać makro, aby utworzyć taki kod.

Upewnij się, że nie analizujesz żadnego z dostarczonych kodów w makrze.

Prostym, przydatnym celem rozszerzenia problemu jest CASE.

(case n 
    (0 (do-this)) 
    (1 (do-that)) 
    (2 (do-something-else))) 

Teraz powinno być łatwe do napisać makro, które rozszerza (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)) do postaci razie ...

4

Nie trzeba eval tutaj, i nie ma potrzeby w zakresie przechowywania wyrażeń na liście.

Prawidłowa realizacja jest następujący:

(defmacro nth-expr (n &rest expressions) 
    `(case ,n 
     ,@(loop for e in expressions 
       for n from 1 
       collect 
       `((,n) ,e)))) 

Twój przykład rozszerza się:

(CASE N ((1) (/ 1 0)) ((2) (+ 1 2)) ((3) (/ 1 0))) 
0

Błąd w drugim przykładzie jest to, że EVAL nie może zobaczyć swoje leksykalnie związana N, X i Y zmienne .

Z CLHS dla EVAL:

Ocenia formularz w bieżącym dynamicznym środowisku i zerowej środowiska leksykalnej.

To działa, jeśli zadeklarowana X i Y jako szczególne:

(let ((n 3) (x "win") (y "lose")) 
    (declare (special x y)) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

ale to jeszcze nie jest tak dobry jak roztwór CASE sugerowanej przez SK-logic.

Powiązane problemy