元编程到底是什么?


128

我正在阅读有关Java平台上的ployglot编程的 TheServerSide文章。本文中的一些评论将元编程称为生成代码的能力(也许是在运行中)。

元编程是在运行时即时生成代码的能力,还是在运行时将方法和属性注入到现有对象中的能力(例如某些动态语言(如Python,Ruby和Groovy所允许的))。


7
你可能有兴趣在这个答案stackoverflow.com/questions/2565572/...
ewernli

@ewernli:这个答案实际上比这里的任何答案都要好!
JD

Answers:


100

元编程是指程序具有自身知识或可以操纵自身的多种方式。

在像C#这样的语言中,反射是元编程的一种形式,因为程序可以检查有关其自身的信息。例如,返回对象的所有属性的列表。

在诸如ActionScript之类的语言中,您可以在运行时评估函数以创建新程序,例如eval(“ x” + i)。DoSomething()在i为1时会影响名为x1的对象,而在i为2时会影响x2的对象。

最后,元编程的另一种常见形式是程序可以以非平凡的方式更改自身。LISP以这一点而闻名,这是Paul Graham在十年前所倡导的。我将不得不查阅他的一些具体论文。但是想法是程序将根据其状态更改程序的另一部分。这提供了一定程度的灵活性,可以在运行时进行决策,这在当今大多数流行语言中都是非常困难的。

还值得注意的是,在进行直接汇编编程的好时光中,在运行时更改自身的程序是必要且非常普遍的。

保罗·格雷厄姆(Paul Graham)的论文《使Lisp与众不同的原因》

许多语言都有一个称为宏的东西。但是Lisp宏是唯一的。信不信由你,他们所做的事与括号有关。Lisp的设计人员并没有将所有括号都用在该语言中只是为了有所不同。对于Blub程序员而言,Lisp代码看起来很奇怪。但是那些括号在那里是有原因的。它们是Lisp和其他语言之间根本不同的外在证据。

Lisp代码由Lisp数据对象组成。从源文件包含字符的意义上讲,字符串并不是该语言支持的数据类型之一。解析器读取后,Lisp代码由您可以遍历的数据结构组成。

如果您了解编译器是如何工作的,那么实际发生的并不是Lisp具有一种奇怪的语法,而是Lisp没有语法。您在解析树中编写程序,这些树在解析其他语言时在编译器中生成。但是您的程序完全可以访问这些分析树。您可以编写操纵它们的程序。在Lisp中,这些程序称为宏。它们是编写程序的程序。

编写程序的程序?您什么时候想这样做?如果您认为在Cobol中,则不会经常出现。一直以来,如果您考虑使用Lisp。如果我可以举一个强大的宏示例并在此处说,那将很方便!那个怎么样?但是,如果我这样做了,那对不了解Lisp的人来说就像是胡言乱语。这里没有空间来解释您需要了解的所有内容以了解其含义。在Ansi Common Lisp中,我试图尽可能快地移动内容,即使如此,直到160页,我才开始使用宏。

但是我想我可以提出一种可能令人信服的论点。Viaweb编辑器的源代码大概是20-25%的宏。宏比普通的Lisp函数更难编写,并且在不需要它们时使用宏被认为是不好的样式。因此该代码中的每个宏都存在,因为必须存在。这意味着该程序中至少有20%至25%的代码正在执行您用其他任何语言都无法轻松完成的工作。无论对Blub程序员持怀疑态度,可能是关于我对Lisp神秘力量的主张,这应该使他感到好奇。我们并不是出于娱乐目的而编写此代码。我们是一家小型的初创公司,我们竭尽全力进行编程,以在我们和竞争对手之间设置技术壁垒。

一个可疑的人可能会开始怀疑这里是否存在某种关联。我们的代码很大一部分正在做其他语言很难做到的事情。最终的软件完成了竞争对手软件无法完成的工作。也许有某种联系。我鼓励您遵循该主题。那个old着拐杖走路的老人可能比目睹更多。


6
不要忘了C ++中的模板元编程。能够在编译时执行表达式和决策,并将结果静态地编译到最终可执行文件中。
雷米·勒博

1
我感到震惊in order to put technical barriers between us and our competitors,这是正确的。
Evan Hu

4
自行操作的程序是所有元程序的子集。一般而言,元编程仅表示对程序进行操作的程序。
JD

55

好问题。非常抱歉,目前没有任何答案能够真正正确回答您的问题。也许我可以帮忙...

元编程的定义确实非常简单:它意味着可以操纵程序的程序。

您接受的答案是说可以自我操纵的程序。这些确实是元程序,但它们是所有元程序的子集。

所有:

  • 解析器
  • 领域特定语言(DSL)
  • 嵌入式领域特定语言(EDSL)
  • 编译器
  • 口译员
  • 任期改写
  • 定理证明

是元程序。因此,GCC编译器是一个元程序,CPython解释器是一个元程序,Mathematica计算机代数系统是一个元程序,Coq定理证明者是一个元程序,依此类推。

其他答案断言元程序是生成其他程序的程序。这些确实是元程序,但同样,它们是所有元程序的子集。的傅立叶变换在西方最快(FFTW)库是这样的元程序的一个例子。源代码主要用OCaml编写,它生成C代码(称为小码),这些C代码组合在一起以创建针对特定机器优化的高性能快速傅里叶变换例程。该库实际上用于在Matlab中提供FFT例程。自从FORTRAN成立以来,人们一直在编写程序来生成数值方法。

1950年代后期,集成了对元编程的支持的第一种编程语言是LISt Processor(LISP)语言。LISP 1.5包含许多使元编程更容易的功能。首先,LISP的核心数据类型是嵌套列表,即像树这样的树(a (b c) d),这意味着任何LISP代码都可以本地表示为数据结构。这称为谐音。其次,可以使用QUOTE将LISP代码轻松转换为数据。例如,加(+ 1 2 3)1 + 2 + 3并(QUOTE (+ 1 2 3))创建一个表达式,该表达式在求值时加1 + 2 + 3。第三,LISP提供了一个元循环评估器,使您可以使用主机解释器或编译器在运行时评估LISP代码,包括在运行时生成的LISP代码。LISP的后代包括SchemeClojure。在所有这些语言中,元编程通常以修改自身的程序的形式(通常使用宏)看到。

在1970年代,罗宾·米尔纳(Robin Milner)开发了一种元语言(ML),该语言演变成ML系列编程语言,其中包括Standard MLOCaml,并深受HaskellF#的影响。这些语言使表达其他语言变得容易。在这些语言中,元程序最常以词法分析器,解析器,解释器和编译器的形式出现。

1994年,Erwin Unruh发现C ++模板系统是Turing完整的,可用于在编译时执行任意程序。C ++模板元编程将元编程带给了未受洗的群众,后者无休止地将其用于许多不同的事情,包括在Blitz ++库中生成数值方法。


33

嗯,元编程只是编程,但是基本上是“写代码写代码”

您提到的能力,当程序可以观察和修改其自身的结构和行为时,称为反射,它是元编程的一种。

动态类型的语言具有强大的运行时反射功能,这些语言的解释特性使其成为可能...

静态类型语言还具有强大的元编程技术,例如C ++ 模板元编程 ...


14

这只是我的个人观点,可能是元编程的最宽松定义。

我认为它包括:

  1. 编译代码生成或运行时代码生成(或两者)
  2. 面向方面的思维或面向方面的编程
  3. 思考

我认为您可以通过结合使用以下任一方法来达到目标​​:

  1. 反射
  2. DSL(域特定语言)
  3. 属性(.NET)或注释(Java)
  4. 泛型(.NET / Java)
  5. 范本(C ++)
  6. method_missing(Ruby)
  7. 闭包/一流的功能/代表
  8. AOP-面向方面的编程

非常简洁和周到的答案。给了我很好的调查菜单。谢谢!
swyx

6

元编程正在编写输出另一个程序的程序。像Lisp这样的语言确实很擅长。与像Java这样的语言相比,使用支持真正宏(不是C ++宏,而是可以操纵它们输出的代码的语言)(例如Ruby,Lisp,Scheme等)的语言要容易得多。

一种实现是创建“领域特定语言”,这是一种增强编程语言以完成特定任务的方式。如果正确完成,它的功能将非常强大。Ruby on Rails是这种编程的一个很好的例子。

如果您有兴趣探索这种方法,请查阅《计算机程序结构和解释》,这是涵盖该主题的开创性书籍之一。


5

元编程是计算机程序的编写,这些计算机程序将其他程序(或它们本身)作为数据写入或操作,或者在运行时执行本来可以在编译时完成的部分工作。在许多情况下,这使程序员可以在与手动编写所有代码相同的时间内完成更多工作,或者它为程序提供了更大的灵活性,可以有效地处理新情况而无需重新编译。(来源。)

基本上,编写代码将输出更多代码,运行这些代码可以实现某些目标。通常,这可以使用相同的语言(使用javascript创建一个JavaScript字符串,然后使用eval它)或发出另一种语言(使用.NET创建Windows批处理文件)来完成。


4

维基百科对此主题有一篇不错的文章。一个人不必为某些符合元编程条件的东西进行运行时修改。例如,许多人在编译时使用C ++模板进行元编程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.