簡體   English   中英

在Common Lisp中正確地將列表傳遞給宏

[英]Passing a list to a macro correctly in Common Lisp

我寫了一個宏來做多個嵌套循環。 我知道還有其他設施可以做到這一點,但我正在努力學習如何編寫宏,這似乎是一個很好的用例。 它也有效(有點):

(defmacro dotimes-nested (nlist fn)
  (let ((index-symbs nil)
        (nlist (second nlist))) ;remove quote from the beginning of nlist
    (labels 
      ((rec (nlist)
            (cond ((null nlist) nil)
                  ((null (cdr nlist))
                   (let ((g (gensym)))
                     (push g index-symbs)
                     `(dotimes (,g ,(car nlist) ,g)
                        (funcall ,fn ,@(reverse index-symbs)))))
                  (t (let ((h (gensym)))
                       (push h index-symbs)
                       `(dotimes (,h ,(car nlist) ,h)
                          ,(rec (cdr nlist))))))))
      (rec nlist))))

運行:

(macroexpand-1 '(dotimes-nested '(2 3 5)
                #'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z))))

輸出:

(DOTIMES (#:G731 2 #:G731)
  (DOTIMES (#:G732 3 #:G732)
    (DOTIMES (#:G733 5 #:G733)
      (FUNCALL #'(LAMBDA (X Y Z) (FORMAT T "~A, ~A, ~A~%" X Y Z)) #:G731 #:G732
               #:G733))))

並像這樣調用宏:

(dotimes-nested '(2 3 5)
                #'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z)))

正確返回我的期望:

0, 0, 0
0, 0, 1
0, 0, 2
0, 0, 3
0, 0, 4
0, 1, 0
0, 1, 1
0, 1, 2
0, 1, 3
0, 1, 4
0, 2, 0
0, 2, 1
0, 2, 2
0, 2, 3
0, 2, 4
1, 0, 0
1, 0, 1
1, 0, 2
1, 0, 3
1, 0, 4
1, 1, 0
1, 1, 1
1, 1, 2
1, 1, 3
1, 1, 4
1, 2, 0
1, 2, 1
1, 2, 2
1, 2, 3
1, 2, 4
2

但是,如果我這樣稱呼它:

(let ((dims '(3 4)))
    (dotimes-nested dims
                #'(lambda (x y) (format t "~A, ~A~%" x y))))

我收到一個錯誤:

; in: LET ((DIMS '(3 4)))
;     (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y)))
; 
; caught ERROR:
;   during macroexpansion of (DOTIMES-NESTED DIMS #'(LAMBDA # #)). Use
;   *BREAK-ON-SIGNALS* to intercept.
;   
;    The value DIMS is not of type LIST.

;     (LET ((DIMS '(3 4)))
;       (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y))))
; 
; caught STYLE-WARNING:
;   The variable DIMS is defined but never used.
; 
; compilation unit finished
;   caught 1 ERROR condition
;   caught 1 STYLE-WARNING condition

如何傳入一個不被解釋為符號但是它的值的變量? 我應該使用宏來評估它嗎? 但我無法弄清楚如何。 這兩種情況如何運作,a:用文字'(3 4)調用宏,b:傳入綁定到列表的符號'(3 4)? 我是否需要單獨的宏?

最后,我有一個次要問題。 當我傳入nlist的文字時,我必須使用(second nlist)來獲取列表值。 我理解這是因為'(3 4) (quote (3 4))在被發送到宏之前被擴展為(quote (3 4)) 這是正確的假設嗎? 如果是這樣,這是一般的做法,即 - 使用傳遞給宏的文字列表的第二個值?

在編譯或運行代碼之前擴展宏。 但是,變量DIMS僅在代碼運行時存在。 給宏的參數是文字數據,所以當你這樣做時

(dotimes-nested dims ...)

DIMS不是對變量的引用(它還不存在),而只是一個符號。

調用宏時也不必引用列表,因為無論如何它都是文字數據。 所以你應該把它稱為(dotimes-nested (2 3 4) ...) (並且不需要刪除宏中的任何內容)。

如果確實需要能夠為維度使用(運行時)變量,則應使用常規函數而不是宏。 就像是:

(defun dotimes-nested (nlist function)
  (labels ((rec (nlist args)
             (if (endp nlist)
                 (apply function (reverse args))
                 (destructuring-bind (first . rest) nlist
                   (dotimes (i first)
                     (rec rest (cons i args)))))))
    (rec nlist nil)))

CL-USER> (let ((dims '(3 4)))
           (dotimes-nested dims
                           #'(lambda (x y) (format t "~A, ~A~%" x y))))
0, 0
0, 1
0, 2
0, 3
1, 0
1, 1
1, 2
1, 3
2, 0
2, 1
2, 2
2, 3
NIL

在您對宏的使用中,我發現您引用了文字列表,並且在您的實現中,您實際上在注釋中應用了second ; remove quote from the beginning of nlist ; remove quote from the beginning of nlist

宏接受代碼輸入並將宏函數應用於那些未評估的表達式,這純粹是僅僅引用表面語法的數據,結果是新代碼。 然后可以在使用它的每個地方使用宏來替換此代碼。

擴張只發生一次。 通常,當存儲函數時,函數中的所有宏都會被擴展,並且當使用該函數時,不存在宏的痕跡。

當你有類似的東西:

(dotimes-nested dims
                #'(lambda (x y) (format t "~A, ~A~%" x y))))

宏獲得符號dims 然后,您生成的代碼應該具有dims以便在運行時通過實際計算到列表,因為您不知道在宏擴展時它可能是什么。

我會這樣做而不是:

(dotimes* ((a 3) (b 2) (c 10))
  (format t "~A, ~A, ~A~%" a b c))

(defmacro dotimes* ((&rest binding-initials) &body body)
  (loop :for (binding initial) :in (reverse binding-initials)
        :for result := `(dotimes (,binding ,initial nil) ,@body) 
                    :then `(dotimes (,binding ,initial nil) ,result)
        :finally (return result)))

關於這一點的好處是你可以在這里使用變量:

(defparameter *x* 10) 
(defparameter *y* 10)
(dotimes* ((x *x*) (y *y*))
  (format t "(~a,~a)~%" x y))

如果你有一個動態數量的變量然后使它成為一個函數,比如@ kiiski的答案,那將是一個更好的匹配。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM