“a”是什么;编写其他程序的Lisp程序;?

“a”是什么;编写其他程序的Lisp程序;?,lisp,Lisp,在通读的过程中,我对Lisp越来越好奇 在中,他提到最强大的功能之一是您可以编写编写其他程序的程序 我在他的网站或其他地方找不到直观的解释。是否有一些最小的Lisp程序显示了如何做到这一点的例子?或者,你能用语言解释一下这到底是什么意思吗?Lisp是什么意思。下面是一个函数,它构建了一个表示和的s表达式 (defun makes(x) (list '+ x 2)) 因此(make5)计算为(+52),这是一个有效的s表达式。你可以把它传给我 还有更复杂的Lisp宏示例。另见。阅读Common

在通读的过程中,我对Lisp越来越好奇

在中,他提到最强大的功能之一是您可以编写编写其他程序的程序

我在他的网站或其他地方找不到直观的解释。是否有一些最小的Lisp程序显示了如何做到这一点的例子?或者,你能用语言解释一下这到底是什么意思吗?

Lisp是什么意思。下面是一个函数,它构建了一个表示和的s表达式

(defun makes(x) (list '+ x 2))
因此
(make5)
计算为
(+52)
,这是一个有效的s表达式。你可以把它传给我

还有更复杂的Lisp宏示例。另见。阅读Common Lisp HyperSpec的上一节(另请注意其表单)。意识到

我强烈建议大家阅读(可以免费下载)。你也可以在网上阅读J.Pitrat的博客


顺便说一句,使用POSIX上的C,您也可以编写程序C代码(或使用或),将生成的代码编译为插件,并使用它。

虽然同源性是使这一过程变得简单的基本属性,但在实践中,许多Lisp中都提供了宏工具,这是一个很好的例子。同源性允许您编写lisp函数,这些函数接受lisp源(表示为列表列表),并对其执行列表操作以生成其他lisp源。宏是用于执行此操作的普通lisp函数,它作为语言语法的扩展安装到lisp的编译器/计算器中。宏像普通函数一样被调用,但编译器不会等到运行时才将宏参数的原始代码传递给它。然后,宏负责返回一些替代代码,供编译器代替它进行处理

一个简单的例子是内置的
when
宏,类似这样使用(假设某个变量
x
):

when
类似于更基本的
if
,但其中
if
接受3个子表达式(test、then case、else case)
接受测试,然后在“then”情况下运行任意数量的表达式(在else情况下返回
nil
)。要使用
if
编写此代码,您需要一个显式块(Common Lisp中的
progn
):

when
版本转换为
if
版本是一些非常简单的列表操作:

(defun when->if (when-expression)
  (list 'if
        (second when-expression)
        (append (list 'progn)
                (rest (rest when-expression)))))
虽然我可能会使用列表模板语法和一些较短的函数来实现这一点:

(defun when->if (when-expression)
  `(if ,(second when-expression) (progn ,@(cddr when-expression)) nil))
这样调用:
(when->if(list'when(list'evenp'x)…)

现在,我们需要做的就是通知编译器,当它看到像
(when…
)这样的表达式时(实际上我正在为
(my when…
)编写一个表达式,以避免与内置版本冲突),它应该使用类似于
when->if
的东西将其转换为它能理解的代码。实际的宏语法实际上允许您将表达式/列表(“destructure”it)分解为宏参数的一部分,因此它最终看起来如下所示:

(defmacro my-when (test &body then-case-expressions)
  `(if ,test (progn ,@then-case-expressions) nil))
看起来有点像一个常规函数,只是它接受代码并输出其他代码。现在我们可以编写
(我的时间(evenpx)…
,一切都正常了


lisp宏工具是lisp表达能力的一个主要组成部分,它允许您对语言进行建模,以更好地适应您的项目,并抽象出几乎所有的样板文件。宏可以简单到
,也可以复杂到足以让第三方OOP库感觉像是语言的一流部分(事实上,许多lisp仍然将OOP实现为纯lisp库,而不是核心编译器的一个特殊组件,这一点您无法从使用宏中看出)。

lisp宏就是一个很好的例子。它们不会被计算,而是转换为其中的表达式。这就是为什么他们本质上是编写程序的程序。它们在编译时和运行时之间转换其中的表达式。这意味着您基本上可以创建自己的语法,因为宏实际上没有计算。一个很好的例子是这种无效的公共lisp表单:

(backwards ("Hello world" nil format))
显然,format函数的语法是向后的。但是我们将它传递给一个未计算的宏,因此不会得到回溯错误,因为该宏实际上未计算。下面是我们的宏的外观:

 (defmacro backwards (expr)
   (reverse expr))
如您所见,我们在宏中反转了表达式,这就是为什么它在编译时和运行时之间成为标准Lisp格式的原因。我们用一个简单的例子从本质上改变了Lisp的语法。对宏的调用不会计算,但会被转换。更复杂的示例是使用html创建网页:

(defmacro standard-page ((&key title href)&body body)
   `(with-html-output-to-string (*standard-output* nil :prologue t :indent t)
       (:html :lang "en"
      (:head
        (:meta :charset "utf-8")
        (:title ,title)
        (:link :rel "stylesheet"
           :type "text/css"
           :href ,href))
     ,@body)))
我们基本上可以创建一个宏,对该宏的调用将不会被计算,但它将扩展为有效的lisp语法,并将被计算。如果我们看一下宏展开,我们可以看到展开就是所评估的:

(pprint (macroexpand-1 '(standard-page (:title "Hello"
                     :href "my-styles.css")
              (:h1 "Hello world"))))
扩展至:

(WITH-HTML-OUTPUT-TO-STRING (*STANDARD-OUTPUT* NIL :PROLOGUE T :INDENT T)
  (:HTML :LANG "en"
     (:HEAD (:META :CHARSET "utf-8") (:TITLE "Hello")
     (:LINK :REL "stylesheet" :TYPE "text/css" :HREF "my-styles.css"))
     (:H1 "Hello world")))

这就是为什么Paul Graham提到基本上可以编写编写程序的程序,而ViaWeb本质上就是一个大宏。像这样的一堆宏可以编写代码,可以编写代码…

googleforlisp宏和
eval
我想任何一种语言都可以编写其他程序,这取决于你想写的程度:我认为更好的问题是为什么这么多人没有学过这个(例如,为什么SICP是未知的)。。。。我真的不知道,这让我喜欢javascript中的
constmakes=x=>x+'+2'
?我想我不会喜欢维护一个依赖于生成自己代码的代码库。不幸的是,homoignic只意味着程序存储在与输入相同的表示形式中。这意味着类似于shell的Bash是同源的,因为您可以看到使用
set
定义的所有函数
(pprint (macroexpand-1 '(standard-page (:title "Hello"
                     :href "my-styles.css")
              (:h1 "Hello world"))))
(WITH-HTML-OUTPUT-TO-STRING (*STANDARD-OUTPUT* NIL :PROLOGUE T :INDENT T)
  (:HTML :LANG "en"
     (:HEAD (:META :CHARSET "utf-8") (:TITLE "Hello")
     (:LINK :REL "stylesheet" :TYPE "text/css" :HREF "my-styles.css"))
     (:H1 "Hello world")))