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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是“编写其他程序的Lisp程序”?相关的知识,希望对你有一定的参考价值。

在阅读Paul Graham's Essays时,我对Lisp越来越好奇。

this article中,他提到最强大的功能之一是你可以编写编写其他程序的程序。

我无法在他的网站或其他地方找到直观的解释。是否有一些最小的Lisp程序,它显示了如何完成此操作的示例?或者,你能用语言解释这究竟意味着什么吗?

答案

Lisp是homoiconic。这是一个构建表示和的s表达式的函数。

(defun makes(x) (list '+ x 2))

所以(makes 5)评估(+ 5 2)这是一个有效的s表达式。你可以将它传递给eval

Lisp宏有更复杂的例子。另见this。阅读关于Common Lisp HyperSpec的Evaluation and Compilation的部分(另请注意其compiledefmacroeval表格)。注意multi-staged programming

我强烈建议阅读SICP(可免费下载)然后Lisp In Small Pieces。您还可以在Gödel, Escher, Bach....上阅读Bootstrapping Artificial Intelligence和J.Pitrat的博客。

顺便说一下,在POSIX上使用C语言,您也可以编写程序generating C代码(或使用GCCJITLLVM),将生成的代码编译为插件,并对其进行dlopen

另一答案

虽然同质性是使这变得容易的基本属性,但实际上这是一个很好的例子,它存在于许多lisps中。 Homoiconicity允许您编写带有lisp源(表示为列表列表)的lisp函数,并对其执行列表操作操作以生成其他lisp源。宏是一个简单的lisp函数,用于执行此操作,它作为语言语法的扩展安装到lisp的编译器/赋值器中。宏被调用为普通函数,但不是等到运行时编译器将宏参数的原始代码传递给它。然后,宏负责返回一些替代代码,供编译器在其位置进行处理。

一个简单的例子是内置的when宏,就像这样使用(假设有一些变量x):

(when (evenp x)
  (print "It's even!")
  (* 5 x))

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

(if (evenp x)
    (progn
      (print "It's even!")
      (* 5 x))
    nil)

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”它)作为宏的参数的一部分,所以它最终看起来像这样:

(defmacro my-when (test &body then-case-expressions)
  `(if ,test (progn ,@then-case-expressions) nil))

看起来像一个常规函数,除了它采取代码和输出其他代码。现在我们可以写(my-when (evenp x) ...),一切正常。

lisp宏设施构成了lisps表达能力的一个主要组成部分 - 它们允许您模拟语言以更好地适应您的项目并抽象几乎任何样板。宏可以像when一样简单或复杂到足以使第三方OOP库感觉像语言的第一类部分(实际上许多lisps仍然将OOP实现为纯lisp库而不是核心的特殊组件编译器,而不是你可以告诉他们使用它们)。

另一答案

一个很好的例子是Lisp宏。它们不会被评估,而是转换为其中的表达式。这就是使他们成为编写程序的程序的原因。它们在编译时和运行时之间转换它们中的表达式。这意味着您实际上可以创建自己的语法,因为实际上没有评估宏。一个很好的例子是这个无效的常见lisp形式:

(backwards ("Hello world" nil 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")))

这就是保罗格雷厄姆提到你基本上可以编写编写程序的程序的原因,而ViaWeb本质上就是一个大宏。像这样编写代码的一堆宏可以编写可以编写代码的代码......

以上是关于什么是“编写其他程序的Lisp程序”?的主要内容,如果未能解决你的问题,请参考以下文章

此 Canon SDK C++ 代码片段的等效 C# 代码是啥?

有人知道下面的代码片段是啥意思吗?

iphone / Objective c的最佳代码片段网站是啥[重复]

这个代码片段有啥作用?

在这个 spark 代码片段中 ordering.by 是啥意思?

简单的方法来分享/讨论/协作的代码片段?