[Scheme] define-syntax 佮 syntax-rules 的使用例
8.6

[Scheme] define-syntax 佮 syntax-rules 的使用例

佇 Scheme 中,巨集 (macro) 的使用是一个特點,巨集佮函數真成,毋過會當處理 syntax (句法)做輸入的 argumemt(引數)。巨集的定義干涉著 syntax。佇伊的囝語言 Racket 中閣有 syntax-case 等語法,提來定義 syntax 用的。

若定義巨集,用 syntax-rules 通定義巨集當處理幾个 argument 的時愛按怎執行。比論以下的例:

;; #lang r5rs -> scheme R5Rs
;; scheme 系 (define-syntax) (syntax-rule) 的用法舉隅(有關巨集 macro)
 
;;; 求算平方和,sqrt-num 是巨集名稱
;; 期望輸出結果
; (sqrt-sum 2) => 4 (回傳值,= 2^2)
; (sqrt-sum 1 3) => 10 (=1^2+3^2)
; (sqrt-sum 1 3 5) => 35 (=1^2+3^2+5^2)
(define-syntax sqrt-sum
  (syntax-rules ()
    ((_ x) ; 一元引數時。底線_代表 sqrt-num 這個函數,當然也可以直接寫成 sqrt-num。下同
     (* x x)) ; 處理方法
 
    ((_ x y ...) ; 二元以上引數
     (+ (_ x)(_ y ...))) ; 處理方法,可遞迴表示
  )
)
 
;; 另一種寫法,利用尾遞迴來避免堆棧耗用過度
(define-syntax sqrt-sum
  (syntax-rules ()
    ((_ x) ; 一元引數時。底線_代表 sqrt-num 這個函數,當然也可以直接寫成 sqrt-num。下同
     (* x x)) ; 處理方法
 
    ((_ x y ...) ; 二元以上引數
     (begin ; 執行多個指令
       ; 定義尾遞迴表示
       (define (sqrt-sum-iter a arg-list)
       (if (eq? arg-list '()) a ;  arg-list 為空,傳回值
           ;; else  arg-list 頭,將頭的平方和加到 a
           (sqrt-sum-iter
            (+ a (* (car arg-list) (car arg-list)))
            (cdr arg-list))
       )
      )
     (sqrt-sum-iter 0 (list x y ...))) ; 處理方法,可用尾遞迴表示,避免堆棧使用過多
     )
  )
)
 
;; syntax-case 的使用,在 racket 可以用,但 Drracket 附隨的 scheme r5rs不行
;(define-syntax sqrt-sum
;   (lambda (stx) ;; 要加
;     (syntax-case stx ()
;       ((_ x)
;        (syntax (* x x))) ;; 執行的動作,要包 syntax
;       ((_ x y ...)
;        (syntax (+ (sqrt-sum x) (sqrt-sum y ...)))) ;; syntax 內不能使用 _ 表示原本函數
;       )))
 
; syntax-case 的使用,在 racket 可以用,但 Drracket 附隨的 scheme r5rs不行
; 另一種寫法,stx 表示 sqrt-num 的參數
;(define-syntax (sqrt-sum stx)
;   (syntax-case stx () (
;       ((_ x) ; lambda (stx) 等不要加
;       (syntax (* x x))) ;; 執行的動作,要包 syntax
;       ((_ x y ...)
;       (syntax (+ (sqrt-sum x) (sqrt-sum y ...)))) ;; syntax 內不能使用 _ 表示原本函數
;       ))
 
;; 其他關於 macro 可以參考:https://artyom.me/learning-racket-2