我正在读的第一本书说:
编程语言中最有趣的发展之一是创建可扩展语言--在程序中可以更改其语法和语义的语言。最早也是最常见的语言扩展方案之一是宏定义。
请给出一个扩展Lisp编程语言语法和语义的Lisp宏的示例(以及解释)。
1“解析、翻译和编译的理论”,第1卷,Aho和Ullman著,第58页。
发布于 2022-05-21 13:27:25
想象一下这个场景:1958年,FORTRAN刚刚被发明。只有在原子测试的余辉下,原始Lisp程序员才用原语Lisp编写循环,就像原始FORTRAN程序员通常有的那样:
(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年从未来。“看,”他们说,“我们可以写这个奇怪的未来的东西”:
(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>)))“现在”,他们说,“你可以写这个”:
(sloop (i 0 10)
(do-hard-sums i))于是就发明了简单的循环。
回到这里以后,我们可以看到这个循环扩展为什么:
(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程序员用来手工输入的代码。我们可以做这个:
> (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的预定义宏之一,我们可以看到它扩展为什么:
(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是一种可编程编程语言。
发布于 2022-05-21 12:36:17
嗯,也许解释将是简洁的,但您可以查看lisp语言本身使用的宏,例如defun。
defun.htm
在lisp中,宏是语言本身的一个重要部分,基本上允许您在编译代码之前重写它。
发布于 2022-06-14 09:12:17
不仅仅是由Def宏定义的宏。还有读卡器宏!正如保罗·格雷厄姆在李斯特中所说:
Lisp表达式生命中的三个重要时刻是读取时、编译时和运行时。函数在运行时处于控制状态。宏使我们有机会在编译时对程序执行转换.…读宏…在阅读时间做他们的工作。 宏和读宏在不同的阶段看到你的程序.当程序已经被读取器解析为Lisp对象时,宏就会得到它,并且读宏会在程序仍然是文本的时候对它进行操作。但是,通过调用文本上的read,一个read宏,如果它愿意,也可以得到解析的Lisp对象。因此,读宏至少和普通宏一样强大。
使用Reader宏,您可以定义新的语义方式,而不是普通宏,例如:
https://stackoverflow.com/questions/72329565
复制相似问题