在lisp中创建等效于incf作为宏函数

时间:2017-05-04 01:41:49

标签: macros lisp common-lisp

我刚开始学习宏功能的概念。

我的老师要求我们创建一个功能与incf完全相同的宏功能。

以下是他为pop提供的一个例子

(defmacro mypop (nom) 
   (list 'prog1 (list 'car nom) (list 'setq nom (list 'cdr nom))) )

这是我试图变成宏的常规函数​​:

(defun iincf (elem &optional num )
   (cond
      ((not num) (setq elem (+ 1 elem)))
      (t (setq elem (+ num elem))) ) )

这是我尝试将其变成宏:

(defmacro myincf (elem &optional num )
   (list 'cond
      ((list 'not num) (list 'setq elem (list '+ 1 elem)))
      (t (list 'setq elem (list '+ num elem))) ) )

然而,我收到此错误,我不知道原因:

*** - system::%expand-form: (list 'not num) should be a lambda expression

另外,我不确定我的函数是否会实际更改顶级变量的值。

所以这是我的两个问题:

  1. 为什么会出现此错误?
  2. 我正在尝试将函数变成宏吗? (如果成功将其转换为宏功能,它会按照我的意图行事吗?)
  3. PS:我知道这个练习可能会违反lisp中的许多常见规则,但这只是为了练习。谢谢! :)

1 个答案:

答案 0 :(得分:4)

错误的原因是您的语法无效:

((list ...) ...)
(t (list ...))

第一个元素应该是函数名或lambda表达式,因此您需要将其更改为

(list (list ...) ...)
(list t (list ...))

虽然宏还不是一个非常好的宏。首先,backquote语法会使代码更具可读性。它允许您编写仅评估指定表单的模板。例如,给定的MYPOP宏看起来像

(defmacro mypop (nom)
  `(prog1 (car ,nom)
     (setq ,nom (cdr ,nom))))

仅评估在它们之前带有逗号的表单。与您的宏相同:

(defmacro myincf (elem &optional num)
  `(cond
     ((not ,num) (setq ,elem (+ 1 ,elem)))
     (t (setq ,elem (+ ,num ,elem)))))

COND不应该成为扩张的一部分。它应该在宏展开期间进行评估,并且只返回其中一个分支的SETQ表单。

(defmacro myincf (elem &optional num)
  (cond
    ((not num) `(setq ,elem (+ 1 ,elem)))
    (t `(setq ,elem (+ ,num ,elem)))))

两个分支之间的唯一区别是第一个分支默认为1 NUM。实现相同目标的一种更简单的方法是给NUM一个默认值。

(defmacro myincf (elem &optional (num 1))
  `(setq ,elem (+ ,num ,elem)))

当然,标准INCF有点复杂,因为它适用于各种地方(不仅仅是变量),并确保地点的子表单只被评估一次。但是,由于MYPOP示例并未处理这些问题,因此我不认为您必须这样做。

如果您愿意,可以使用一种简单的方法来定义这样的宏

(define-modify-macro myincf (&optional (num 1)) +)

或者您可以使用

等手动进行相同的操作
(defmacro myincf (place &optional (num 1) &environment env)
  (multiple-value-bind (dummies vals store setter getter)
      (get-setf-expansion place env)
    `(let* (,@(mapcar #'list dummies vals)
            (,(first store) (+ ,getter ,num)))
       ,setter)))

但在实际程序中使用DEFINE-MODIFY-MACRO会更受欢迎(代码更短,错误更少)。如果您有兴趣,可以阅读GET-SETF-EXPANSIONDEFINE-MODIFY-MACRO