Scheme的宏比Lisp的宏簡單,但是它有些看起來奇怪的“語法”卻很少有文章進行過解釋或者文章說了這點卻容易忽略,使得我以前對Scheme宏的學習一直摸不到頭腦,在看了很多篇有關Scheme宏的介紹文章后,覺得這篇文章寫的是最容易理解的了,雖然不能算淺顯易懂,有可能宏這個東西說得淺顯了就不太容易懂,原文地址:Syntax宏 · 大專欄 (dazhuanlan.com) ,另外一篇Scheme官方介紹宏使用的文章鏈接:Syntactic Extension (scheme.com)
正文開始---------
宏 是 用戶自定義的語法 ,而 Lisp/Scheme 提供的宏遠比其他編程語言要強大的多, 使用宏可以讓代碼漂亮和緊湊
本質上來說宏就是一種 代碼轉換器 : 代碼在被解釋或編譯前被轉換成另外一種形式去執行
在 Scheme 語言中, 在 R5R5 規范以后簡單的宏可以被方便地使用 syntax-rules 形式來定義,作為對比 Common Lisp 的宏顯得要復雜許多:
- 使用 syntax-rules 可以更 直接 地定義宏,而不需要考慮諸如 變數捕捉 等細節
- 定義復雜的宏,使用 syntax-rules 比起 Common Lisp 的宏來說會困難得多(某些 Scheme 實作提供了 define-macro )
簡單宏
把某個變數賦值為 '()
(define-syntax nil!
(syntax-rules ()
;; 轉換前和轉換后的串列
((_ x) ;; 轉換前的代碼,_ 表示 宏的名字
(set! x '())))) ;; 轉換后的代碼
;; (define a 1)
;; a ; => 1
;; (nil! a)
;; a ; => ()
syntax-rules 的 第二個引數 是一個兩個元素的 串列 :
- 第一個元素:轉換前的代碼,其中 _ 代表宏的名字
- 第二個元素:轉換后的代碼
注意:如果把上面的代碼可以寫成函式,但是因為閉包的原因,傳遞進去的引數實際上是不會改變的
(define (f-nil! x) (set! x '())) ;; (define a 1) ;; a ; => 1 ;; (f-nil! a) ; => () ;; a ; => 1 ;; (set! a '()) ;; a ; => ()
當謂詞為真的時候,對接下來的運算式求值:
(define-syntax when
(syntax-rules ()
((_ pred b1 ...) ; ... 含義是任意個運算式,可以是0個
(if pred (begin b1 ...)))))
;; (let ((i 0))
;; (when (= i 0)
;; (display "i == 0")
;; (newline)))
;; => i == 0
;; ;Unspecified return value
上面的代碼無法用函式來寫,因為這是把代碼轉換到另外一種形式
可以用定義好的宏來再次定義宏:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; while宏:表示條件成立的回圈 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax while
(syntax-rules ()
((_ pred b1 ...)
(let loop () (when pred b1 ... (loop))))))
;; (let ((i 0))
;; (while (< i 10)
;; (display i)
;; (display #Space)
;; (set! i (+ i 1))))
;; => 0 1 2 3 4 5 6 7 8 9
;; ;Unspecified return value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; for宏:表示數字在范圍之內的回圈 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax for
(syntax-rules ()
((_ (i from to) b1 ...)
(let loop((i from))
(when (< i to)
b1 ...
(loop (1+ i)))))))
;; (for (i 0 10)
;; (display i)
;; (display #Space))
;; => 0 1 2 3 4 5 6 7 8 9
;; ;Unspecified return value
多種模式
syntax-rules 可以支持定義多種模式,incf 宏是增加變數的值,如果不傳入增加的值,就默認增加 1, 如果給定,就增加給定的值:
;; incf宏
(define-syntax incf
(syntax-rules ()
((_ x) (begin (set! x (+ x 1)) x)) ; 如果不給增加引數,默認增加1
((_ x i) (begin (set! x (+ x i)) x))))
;; (let ((i 0) (j 0))
;; (incf i)
;; (incf j 3)
;; (display (list 'i '= i))
;; (newline)
;; (display (list 'j '= j)))
;; => (i = 1)
;; (j = 3)
;; ;Unspecified return value
遞回定義
syntax-rules 支持遞回定義宏:
(define-syntax my-and
(syntax-rules ()
((_) #t)
((_ e) e)
((_ e1 e2 ...)
(if e1
(my-and e2 ...)
#f))))
;; (my-and) ; => #t
;; (my-and #f) ; => #f
;; (my-and (> 2 1)) ; => #t
;; (my-and #t #f) ; => #f
;; (my-and #t (> 2 1)) ; => #t
;; (my-and #t (> 2 1) (< 3 2) (= 1 1))
(define-syntax my-or
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e1 e2 ...)
(let ((t e1))
(if t t (my-or e2 ...))))))
;; (my-or) ; => #f
;; (my-or #t) ; => #t
;; (my-or (< 2 1)) ; => #f
;; (my-or #f #f) ; => #f
;; (my-or #f (> 2 1)) ; => #t
;; (my-or #f (> 2 1) (< 3 2) (= 1 1)) ; => #t
保留關鍵字
syntax-rules 的第一個引數是一組 保留關鍵字 的串列,這些關鍵字在轉換的時候不會被替換,下面是自定義的 my-cond 宏, else 就是這個宏的保留關鍵字:
(define-syntax my-cond
(syntax-rules (else)
((_ (else e1 ...))
(begin e1 ...))
((_ (e1 e2 ...))
(when e1 e2 ...))
((_ (e1 e2 ...) c1 ...)
(if e1
(begin e2 ...)
(cond c1 ...)))))
;; (my-cond (else (+ 1 2))) ; => 3
;; (my-cond ((> 1 0) (+ 1 2))) ; => 3
;; (my-cond ((< 1 0) (+ 1 2))) ; => ;Unspecified return value
;; (my-cond ((< 1 0) (+ 1 2))
;; ((> 1 0) (+ 2 3))) ; => 5
;; (my-cond ((< 1 0) (+ 1 2))
;; (else (+ 2 3))) ; => 5
區域宏
let-syntax 和 letrec-syntax 可以被用來定義函式中的 區域宏
《SOD框架“企業級”應用資料架構實戰》
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/305844.html
標籤:其他
上一篇:ApplicationRunner- 實作專案啟動就執行的功能
下一篇:🥶全套Java教程_Java基礎入門教程,零基礎小白自學Java必備教程🐱?009 # 第九單元 封裝、構造方法 #
