【问题标题】:Creating equivalent to incf as macro-function in lisp在 lisp 中创建等效于 incf 的宏函数
【发布时间】:2017-05-04 01:41:48
【问题描述】:

我刚刚开始学习宏函数的概念。

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

这是他给我们的流行音乐示例

(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. 我试图将其转换为宏的函数是否正常? (如果成功地把它变成一个宏函数,它会做我打算做的吗?)

PS:我知道这个练习可能会违反 lisp 中的许多常见规则,但这只是为了练习。谢谢! :)

【问题讨论】:

  • 我只想告诉你Portacle,一个可移植的多平台 Common Lisp 开发环境(包含 Emacs、SBCL、Slime、Quicklips 和 Git),因为它是获得优秀开发人员的最简单方法环境,我知道我确实很高兴找到它 :) 以及关于 lisp-lang.org,一个吸引人和现代网站在 CL 上,以防你认为它不存在(就像我一样)!

标签: macros lisp common-lisp


【解决方案1】:

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

((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 for 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

【讨论】:

  • 感谢您的详细而有用的回答。我花了几天时间研究它并学习了相应的语法,但是非常有帮助。
猜你喜欢
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
相关资源
最近更新 更多