我正在阅读 TheServerSide 上关于 Java 平台上的多语言编程的文章。文章中的一些评论将元编程称为生成代码的能力(可能是动态的)。
元编程是动态生成代码的能力,还是在运行时将方法和属性注入现有对象的能力(就像 Python、Ruby 和 Groovy 等一些动态语言所允许的那样)。
我正在阅读 TheServerSide 上关于 Java 平台上的多语言编程的文章。文章中的一些评论将元编程称为生成代码的能力(可能是动态的)。
元编程是动态生成代码的能力,还是在运行时将方法和属性注入现有对象的能力(就像 Python、Ruby 和 Groovy 等一些动态语言所允许的那样)。
元编程是指程序了解自身或操纵自身的各种方式。
在像 C# 这样的语言中,反射是一种元编程形式,因为程序可以检查有关自身的信息。例如,返回一个对象所有属性的列表。
在 ActionScript 等语言中,您可以在运行时评估函数以创建新程序,例如 eval("x" + i)。当 i 为 1 时 DoSomething() 将影响名为 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 的神秘力量持怀疑态度,这应该让他很好奇。我们写这段代码不是为了自娱自乐。我们是一家很小的初创公司,为了在我们和竞争对手之间设置技术障碍,我们尽可能地努力编程。
一个可疑的人可能会开始怀疑这里是否存在某种关联。我们的大部分代码都在做其他语言很难做到的事情。由此产生的软件做了我们竞争对手的软件做不到的事情。也许有某种联系。我鼓励您关注该主题。那个拄着拐杖蹒跚而行的老人可能比我们看到的更多。
好问题。很遗憾看到目前没有一个答案能真正正确回答您的问题。或许我可以帮...
元编程的定义真的很简单:它意味着操纵程序的程序。
您接受的答案说程序会操纵自己。这些确实是元程序,但它们是所有元程序的子集。
全部:
是元程序。所以GCC 编译器是一个元程序,CPython 解释器是一个元程序,Mathematica 计算机代数系统是一个元程序,Coq 定理证明器是一个元程序等等。
其他答案断言元程序是生成其他程序的程序。这些确实是元程序,但同样,它们是所有元程序的子集。West (FFTW) 库中的最快傅立叶变换就是这种元程序的一个例子。源代码大部分是用OCaml编写的,它会生成一些 C 代码(称为 codelet),这些代码组合起来可以创建针对特定机器优化的高性能快速傅立叶变换例程。该库实际上用于在 Matlab 中提供 FFT 例程。自FORTRAN早期以来,人们一直在编写程序来生成数值方法数十年。
第一种集成了元编程支持的编程语言是 1950 年代后期的 LISt 处理器 (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 的后代包括Scheme和Clojure. 在所有这些语言中,元编程最常见的形式是修改自身的程序,通常使用宏。
在 1970 年代,Robin Milner 开发了一种MetaLanguage (ML),该语言演变为 ML 编程语言家族,其中包括标准 ML和OCaml,并强烈影响了Haskell和F#。这些语言使表达其他语言变得容易。在这些语言中,元程序最常见的形式是词法分析器、解析器、解释器和编译器。
1994 年,Erwin Unruh 发现 C++ 模板系统是图灵完备的,可以用来在编译时执行任意程序。C++ 模板元编程将元编程带给了未洗过的大众,他们(ab)将它用于许多不同的事情,包括在Blitz++ 库中生成数值方法。
好吧,元编程只是编程,但它基本上是“写代码的代码”。
您提到的能力,当程序可以观察和修改自己的结构和行为时,称为反射,它是一种元编程。
动态类型语言,具有强大的运行时反射特性,这些语言的解释特性使之成为可能......
静态类型语言还具有强大的元编程技术,例如 C++模板元编程...
这只是我个人的看法,这可能是元编程最自由的定义。
我认为包括:
我认为您可以通过使用其中任何一种并结合使用来实现:
元编程是编写或操作其他程序(或它们本身)作为它们的数据的计算机程序,或者在运行时完成部分工作,否则这些工作将在编译时完成。在许多情况下,这允许程序员在与手动编写所有代码相同的时间内完成更多工作,或者它为程序提供了更大的灵活性,无需重新编译即可有效地处理新情况。(来源。)
基本上,它是在编写输出更多代码的代码,这些代码是为了完成某个目标而运行的。这通常在同一种语言中完成(使用 javascript 创建一个 javascript 字符串,然后使用eval
它)或发出另一种语言(使用 .NET 创建一个 Windows 批处理文件)。
元编程是编写一个输出另一个程序的程序。这是像 Lisp 这样的语言非常擅长的。在 Ruby、Lisp、Scheme 等支持真正宏(不是 C++ 宏,而是可以操作它们输出的代码)的语言中比在 Java 等语言中更容易做到这一点。
一种实现是创建“领域特定语言”,这是一种增强编程语言以完成特定任务的方式。如果操作正确,它可能会非常强大。Ruby on Rails 就是这种编程的一个很好的例子。
如果您有兴趣探索这种方法,请查看计算机程序的结构和解释,这是涵盖该主题的开创性书籍之一。
维基百科有一篇关于这个主题的好文章。不必为某些东西进行运行时修改就可以称为元编程。例如,许多人在编译时使用 C++ 模板进行元编程。
让我们用简单的例子来理解这一点!
template<class T>
class Item{
private:
std::string name;
T value;
public:
Item(std::string name, T value)
: name{name}, value{value} {}
std::string get_name() const {return name;}
T get_value() const {return value;}
};
在这个例子中,T 可以是任何类型的值。例如,在这种情况下,我们期望整数或双精度数。现在,这将编译,但不会生成任何代码。它只是一个蓝图。当用户使用模板或蓝图的专用版本时,编译器将使用模板或蓝图生成代码。这就是元编程的全部内容!