首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Lisp宏如何扩展Lisp编程语言的语法和语义?

Lisp宏如何扩展Lisp编程语言的语法和语义?
EN

Stack Overflow用户
提问于 2022-05-21 12:30:27
回答 3查看 98关注 0票数 0

我正在读的第一本书说:

编程语言中最有趣的发展之一是创建可扩展语言--在程序中可以更改其语法和语义的语言。最早也是最常见的语言扩展方案之一是宏定义。

请给出一个扩展Lisp编程语言语法和语义的Lisp宏的示例(以及解释)。

1“解析、翻译和编译的理论”,第1卷,Aho和Ullman著,第58页。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-05-21 13:27:25

想象一下这个场景:1958年,FORTRAN刚刚被发明。只有在原子测试的余辉下,原始Lisp程序员才用原语Lisp编写循环,就像原始FORTRAN程序员通常有的那样:

代码语言:javascript
复制
(prog ((i 0))                           ;i is 0
  start                                 ;label beginning of loop
  (if (>= i 10)
      (go end))                         ;skip to end when finished
  (do-hard-sums-on i)                   ;hard sums!
  (setf i (+ i 1))                      ;increment i
  (go start)                            ;jump to start
  end)                                  ;end

(当然,这都会出现在大写中,因为小写字母还没有被发明出来,而我写的setf会更丑一些,因为setf (一个宏!)当时也没有发明过)。

进入,在云中只有轻微的有毒烟雾从他们的喷气包,另一个Lisp程序员谁已经逃到1958年从未来。“看,”他们说,“我们可以写这个奇怪的未来的东西”:

代码语言:javascript
复制
(defmacro sloop ((var init limit &optional (step 1)) &body forms)
  (let ((<start> (make-symbol "START")) ;avoid hygiene problems ...
        (<end> (make-symbol "END"))
        (<limit> (make-symbol "LIMIT")) ;... and multiple evaluation problems
        (<step> (make-symbol "STEP")))
    `(prog ((,var ,init)
            (,<limit> ,limit)
            (,<step> ,step))
       ,<start>
       (if (>= ,var ,<limit>)
           (go ,<end>))
       ,@forms
       (setf ,var (+ ,var ,<step>))
       (go ,<start>)
       ,<end>)))

“现在”,他们说,“你可以写这个”:

代码语言:javascript
复制
(sloop (i 0 10)
  (do-hard-sums i))

于是就发明了简单的循环。

回到这里以后,我们可以看到这个循环扩展为什么:

代码语言:javascript
复制
(sloop (i 0 10)
  (format t "~&i = ~D~%" i))
->
(prog ((i 0) (#:limit 10) (#:step 1))
 #:start (if (>= i #:limit) (go #:end))
      (format t "~&i = ~D~%" i)
      (setf i (+ i #:step))
      (go #:start)
 #:end)

这是原始Lisp程序员用来手工输入的代码。我们可以做这个:

代码语言:javascript
复制
> (sloop (i 0 10)
    (format t "~&i = ~D~%" i))
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
nil

事实上,这就是现在Lisp中循环的工作方式。如果我尝试一个简单的do循环,这是Common的预定义宏之一,我们可以看到它扩展为什么:

代码语言:javascript
复制
(do ((i 0 (+ i 1)))
    ((>= i 10))
  (format t "~&i = ~D~%" i))
->
(block nil
  (let ((i 0))
    (declare (ignorable i))
    (declare)
    (tagbody
     #:g1481 (if (>= i 10) (go #:g1480))
             (tagbody (format t "~&i = ~D~%" i)
                      (setq i (+ i 1)))
             (go #:g1481)
     #:g1480)))

嗯,这个扩展是不一样的,它使用了我还没有提到的结构,但是您可以看到重要的事情:这个循环已经重写为使用GO。而且,虽然Common没有定义它的循环宏的扩展,但是几乎可以肯定的是,所有标准的扩展都是这样的(但通常更复杂)。

换句话说: Lisp没有任何原始循环结构,但是所有这些构造都是由宏添加到语言中的。这些宏以及其他宏以其他方式扩展语言,可以由用户编写:它们不必由语言本身提供。

Lisp是一种可编程编程语言。

票数 6
EN

Stack Overflow用户

发布于 2022-05-21 12:36:17

嗯,也许解释将是简洁的,但您可以查看lisp语言本身使用的宏,例如defun

defun.htm

在lisp中,宏是语言本身的一个重要部分,基本上允许您在编译代码之前重写它。

票数 0
EN

Stack Overflow用户

发布于 2022-06-14 09:12:17

不仅仅是由Def宏定义的宏。还有读卡器宏!正如保罗·格雷厄姆在李斯特中所说:

Lisp表达式生命中的三个重要时刻是读取时、编译时和运行时。函数在运行时处于控制状态。宏使我们有机会在编译时对程序执行转换.…读宏…在阅读时间做他们的工作。 宏和读宏在不同的阶段看到你的程序.当程序已经被读取器解析为Lisp对象时,宏就会得到它,并且读宏会在程序仍然是文本的时候对它进行操作。但是,通过调用文本上的read,一个read宏,如果它愿意,也可以得到解析的Lisp对象。因此,读宏至少和普通宏一样强大。

使用Reader宏,您可以定义新的语义方式,而不是普通宏,例如:

  • 添加对字符串内插( cl-国际刑警组织 )的支持
  • 将对JSON的支持直接添加到语言中:请参阅这篇文章了解更多信息。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72329565

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档