Macros Lisp';s宏可用于

Macros Lisp';s宏可用于,macros,lisp,Macros,Lisp,我听说Lisp的宏系统非常强大。然而,我发现很难找到一些实用的例子来说明它们的用途;没有它们就很难实现的事情 有人能举一些例子吗?只是一个猜测——特定于领域的语言。使用宏,您可以定义自己的语法,从而扩展Lisp并使其成为 适合您编写的程序 查看这本非常好的在线书籍,以获取实际示例 选择任意一个“”。阅读他们的例子。这就是它能做的 除非您不需要使用不同的编程语言,将任何宏扩展代码放在使用宏的地方,运行单独的命令来生成,或者在硬盘上有额外的文本文件,这些文件对编译器来说只有价值 例如,我认为阅读这

我听说Lisp的宏系统非常强大。然而,我发现很难找到一些实用的例子来说明它们的用途;没有它们就很难实现的事情


有人能举一些例子吗?

只是一个猜测——特定于领域的语言。

使用宏,您可以定义自己的语法,从而扩展Lisp并使其成为
适合您编写的程序

查看这本非常好的在线书籍,以获取实际示例


选择任意一个“”。阅读他们的例子。这就是它能做的

除非您不需要使用不同的编程语言,将任何宏扩展代码放在使用宏的地方,运行单独的命令来生成,或者在硬盘上有额外的文本文件,这些文件对编译器来说只有价值


例如,我认为阅读这个示例应该足以让任何Lisp程序员哭泣。

源代码转换。各种各样的。示例:

  • 新的控制流语句:需要WHILE语句吗?你的语言没有?为什么要等到仁慈的独裁者明年可能会增加一个呢。你自己写吧。五分钟后

  • 较短的代码:您需要20个看起来几乎相同的类声明-只有有限的位置不同。编写一个宏表单,将差异作为参数,并为您生成源代码。以后要换吗?在一个位置更改宏

  • 源代码树中的替换:是否要将代码添加到源代码树中?变量真的应该是函数调用吗?在“遍历”源代码并更改找到变量的位置的代码周围包装一个宏

  • 后缀语法:是否要以后缀形式编写代码?使用宏将代码重写为正常形式(Lisp中的前缀)

  • 编译时效果:您需要在编译器环境中运行一些代码来通知开发环境有关定义的信息?宏可以生成在编译时运行的代码

  • 编译时的代码简化/优化:您想在编译时简化一些代码吗?使用一个进行简化的宏,这样可以根据源表单将工作从运行时转移到编译时

  • 从描述/配置生成代码:您需要编写复杂的类组合。例如,您的窗口有一个类,子面板有类,窗格之间有空间限制,您有一个命令循环,一个菜单和一大堆其他东西。编写一个宏,捕获窗口及其组件的描述,并根据描述创建驱动应用程序的类和命令

  • 语法改进:某些语言语法看起来不太方便?编写一个宏,使应用程序编写者更方便

  • 特定于域的语言:您需要更接近应用程序域的语言吗?用一堆宏创建必要的语言形式

元语言抽象


基本思想:语言层面的一切(新形式、新语法、形式转换、简化、IDE支持等)现在都可以由开发人员逐件编程-无需单独的宏处理阶段。

除了扩展语言语法以允许您更清楚地表达自己之外,它还可以让您控制评估。尝试用您选择的语言编写您自己的
if
,这样您就可以实际编写
my\u if某物my\u然后打印“success”my\u else打印“failure”
,而不必对这两个打印语句进行评估。在任何严格的语言中,如果没有足够强大的宏系统,这是不可能的。不过,普通的Lisp程序员不会觉得这项任务太有挑战性。对于
for
-循环、
foreach
循环等,也一样。您不能用C来表达这些东西,因为它们需要特殊的求值语义(人们实际上试图将
foreach
引入Objective-C,但效果并不好),但由于其宏,它们在普通Lisp中几乎微不足道。

,标准的统计编程语言具有宏()。您可以使用它来实现函数
lm()
,该函数根据指定为代码的模型分析数据


它的工作原理如下:
lm(Y~aX+b,data)
将尝试找到最适合数据的
a
b
参数。最酷的部分是,你可以用任何线性方程代替aX+b,它仍然可以工作。这是一个让统计计算更容易的绝妙功能,它的工作如此优雅,因为
lm()
可以分析它给出的等式,这正是Lisp宏所做的。

您通常希望在预处理器中完成的任何操作

我写的一个宏用于定义驱动游戏对象的状态机。读取代码(使用宏)比读取生成的代码更容易:

(def-ai ray-ai
  (ground
   (let* ((o (object))
          (r (range o)))
     (loop for p in *players*
           if (line-of-sight-p o p r)
           do (progn
                (setf (target o) p)
                (transit seek)))))
  (seek
   (let* ((o (object))
          (target (target o))
          (r (range o))
          (losp (line-of-sight-p o target r)))
     (when losp
       (let ((dir (find-direction o target)))
         (setf (movement o) (object-speed o dir))))
     (unless losp
       (transit ground)))))
然后是:

(progn
 (defclass ray-ai (ai) nil (:default-initargs :current 'ground))
 (defmethod gen-act ((ai ray-ai) (state (eql 'ground)))
            (macrolet ((transit (state)
                         (list 'setf (list 'current 'ai) (list 'quote state))))
              (flet ((object ()
                       (object ai)))
                (let* ((o (object)) (r (range o)))
                  (loop for p in *players*
                        if (line-of-sight-p o p r)
                        do (progn (setf (target o) p) (transit seek)))))))
  (defmethod gen-act ((ai ray-ai) (state (eql 'seek)))
            (macrolet ((transit (state)
                         (list 'setf (list 'current 'ai) (list 'quote state))))
              (flet ((object ()
                       (object ai)))
                (let* ((o (object))
                       (target (target o))
                       (r (range o))
                       (losp (line-of-sight-p o target r)))
                  (when losp
                    (let ((dir (find-direction o target)))
                      (setf (movement o) (object-speed o dir))))
                  (unless losp (transit ground)))))))

通过将整个状态机生成封装在一个宏中,我还可以确保仅引用已定义的状态,并在情况并非如此时发出警告。

宏对于提供对语言功能的访问至关重要。例如,在TXR Lisp中,我调用了一个函数来捕获带分隔符的延续。但是单独使用它是很尴尬的。因此,有一些宏围绕着它,例如,或,它们为可恢复的、暂停的执行提供了替代模型。它们得到了实施

另一个例子是复杂宏,它提供了定义结构类型的语法。它将其参数编译成
lambda<
1> (macroexpand '(defstruct foo bar x y (z 9) (:init (self) (setf self.x 42))))
(sys:make-struct-type 'foo 'bar '()
                      '(x y z) ()
                      (lambda (#:g0101)
                        (let ((#:g0102 (struct-type #:g0101)))
                          (unless (static-slot-p #:g0102 'z)
                            (slotset #:g0101 'z
                                     9)))
                        (let ((self #:g0101))
                          (setf (qref self x)
                           42)))
                      ())