常见的宏观模式

TODO:也许将解释移到备注并单独添加示例

FOOF

在 Common Lisp 中,有一个广义引用的概念。它们允许程序员将值设置为各种位置,就好像它们是变量一样。使用此功能的宏通常在名称中使用 F-postfix。这个地方通常是宏的第一个参数。

标准的例子: INCFDECFROTATEFSHIFTFREMF

一个愚蠢的例子,一个在一个地方翻转数字商店标志的宏:

(defmacro flipf (place)
  `(setf ,place (- ,place)))

WITH-FOO

获取并安全释放资源的宏通常以 WITH- 前缀命名。宏应该通常使用如下语法:

(with-foo (variable details-of-the-foo...)
  body...)

标准示例: WITH-OPEN-FILEWITH-OPEN-STREAMWITH-INPUT-FROM-STRINGWITH-OUTPUT-TO-STRING

实现这种类型的宏的一种方法是首先实现功能版本,这种方法可以避免名称污染和无意识的多重评估的一些缺陷。例如,实现安全创建窗口小部件并在之后清理的 with-widget 宏的第一步可能是一个函数:

(defun call-with-widget (args function)
  (let ((widget (apply #'make-widget args))) ; obtain WIDGET
    (unwind-protect (funcall function widget)  ; call FUNCTION with WIDGET
      (cleanup widget)                         ; cleanup

因为这是一个函数,所以不用担心函数供应商中的名称范围,并且可以很容易地编写相应的宏:

(defmacro with-widget ((var &rest args) &body body)
  `(call-with-widget (list ,@args) (lambda (,var) ,@body)))

DO-FOO

迭代某些东西的宏通常用 DO 前缀命名。宏语法通常应该是格式

(do-foo (variable the-foo-being-done return-value)
  body...)

标准的例子: DOTIMESDOLISTDO-SYMBOLS

FOOCASE,EFOOCASE,CFOOCASE

与某些案例的输入相匹配的宏通常以 CASE-postfix 命名。通常有一个 E...CASE 变量,如果输入与任何情况不匹配则发出错误信号,C...CASE 表示可持续错误。他们应该有类似的语法

(foocase input
  (case-to-match-against (optionally-some-params-for-the-case)
   case-body-forms...)
  more-cases...
  [(otherwise otherwise-body)])

标准示例: CASETYPECASEHANDLER-CASE

例如,一个宏将字符串与正则表达式匹配,并将寄存器组绑定到变量。使用 CL-PPCRE 表示正则表达式。

(defmacro regexcase (input &body cases)
  (let ((block-sym (gensym "block"))
        (input-sym (gensym "input")))
    `(let ((,input-sym ,input))
       (block ,block-sym
         ,@(loop for (regex vars . body) in cases
                 if (eql regex 'otherwise)
                   collect `(return-from ,block-sym (progn ,vars ,@body))
                 else
                   collect `(cl-ppcre:register-groups-bind ,vars
                                (,regex ,input-sym)
                              (return-from ,block-sym
                                (progn ,@body))))))))

(defun test (input)
  (regexcase input
    ("(\\d+)-(\\d+)" (foo bar)
      (format t "Foo: ~a, Bar: ~a~%" foo bar))
    ("Foo: (\\w+)$" (foo)
      (format t "Foo: ~a.~%" foo))
    (otherwise (format t "Didn't match.~%"))))

(test "asd 23-234 qwe")
; Foo: 23, Bar: 234
(test "Foo: Foobar")
; Foo: Foobar.
(test "Foo: 43 - 23")
; Didn't match.

DEFINE-FOO,DEFFOO

定义事物的宏通常用 DEFINE-DEF -prefix 命名。

标准的例子: DEFUNDEFMACRODEFINE-CONDITION