2011-11-05 49 views
3

我與Lisp的宏第一步......Lisp的宏,做循環「展開」

(defconstant width 7) 
(defconstant height 6) 
... 
; board is a 2D array of width x height 
; and this is my first ever macro: 
(defmacro at (y x) 
    `(aref board ,y ,x)) 
; "board" must be available wherever the macro is used. 

(defun foo (board ...) 
    ... 
    (loop for y from 0 to (1- height) do 
    ; thanks to the "at" macro, this is cleaner: 
    (let ((score (+ (at y 0) (at y 1) (at y 2)))) 
     (loop for x from 3 to (1- width) do 
     (incf score (at y x)) 
     ; ...do something with score 
     (decf score (at y (- x 3))))))) 

的代碼使用了我的第一次宏時,「AT」之一。它發出「訪問指令」從board [y] [x]讀取,因此它只能用於存在「board」的地方,如上面的函數「foo」。

這工作 - 然後我意識到...我可以走得更遠。這兩個嵌套循環是「靜態」約束:對於y,從0到height-1,對於x從3到(width-1)...因此理論上,我可以創建一個發射(展開! )循環代碼中完成的incf和decf指令!

我嘗試這樣做:

(defmacro unroll() 
    (loop for y from 0 to (1- height) do 
    `(setf score (+ (at ,y 0) (at ,y 1) (at ,y 2))) 
    (loop for x from 3 to (1- width) do 
    `(incf score (at ,y ,x)) 
    `(decf score (at ,y (- ,x 3)))))) 

...但失敗 - 「(macroexpand-1「(披))」 顯示我NIL。

我在做什麼錯?

如果不清楚,我想使用兩個嵌套循環,並在外循環的開始和內循環的每次迭代發出「代碼」。

任何幫助最讚賞(我是LISP新手)。

UPDATE:後@larsmans'之類的建議,我成功地將這種變化我的代碼 - 和我極爲滿意,我看到的Lisp的版本我Score4 algorithm成爲第2個最快的實現,僅次於C和C++ (並且比OCaml快!)。

回答

3

你應該collect你內心的宏的loop生成報表,而不是假裝do執行它們:

(defmacro unroll() 
    (loop for y from 0 to (1- height) 
     collect 
      `(begin (setf score (+ (at ,y 0) (at ,y 1) (at ,y 2))) 
        ,@(loop for x from 3 to (1- width) 
          collect `(begin (incf score (at ,y ,x)) 
              (decf score (at ,y (- ,x 3)))))))) 
+1

好極了!非常感謝你,我敬畏LISP宏的力量...... – ttsiodras