元编程 – 自解释代码 – 教程,文章,书籍

我正在研究改进我的编程技巧(实际上我每年都尽力减少吸收,就像我们的Jeff Atwood所说的那样),所以我正在考虑阅读有关元编程和自解释代码的内容。

我正在寻找类似白痴的指南(免费下载书籍,在线资源)。 此外,我想要的不仅仅是普通的wiki页面,还有一些语言不可知或最好是Java示例。

你是否知道有这样的资源可以有效地将所有这些资源付诸实践(我知道经验在这一切中有很多话要说但我有点想要建立避免流程错误决策 – 经验 – 良好决策的经验)?

编辑:

像Pragmatic Programmer这样的例子:

…实现一种迷你语言来控制一个简单的绘图包……该语言由单个字母命令组成。 一些命令后跟一个数字。 例如,以下输入将绘制一个矩形:

P 2 # select pen 2 D # pen down W 2 # draw west 2cm N 1 # then north 1 E 2 # then east 2 S 1 # then back south U # pen up 

谢谢!

欢迎来到元编程的精彩世界:)元编程实际上涉及很多事情。 我会尝试列出我的想法:

  • 。 首先在术语下探讨了扩展编程语言的语法和语义的能力。 有几种语言的结构类似于宏,但选择的选择当然是Lisp 。 如果您对元编程感兴趣,那么理解Lisp和宏系统(以及代码和数据具有相同表示的语言的同音性)绝对是必须的。 如果你想要一个在JVM上运行的Lisp方言,那就去Clojure吧 。 一些资源:

    • Clojure迷你语言
    • 击败平均值(为什么Lisp是秘密武器)

    还有很多关于Lisp的资源。

  • DSL 。 扩展一种语言语法和语义的能力现在在术语“DSL”下重新命名。 创建DSL的最简单方法是使用解释器模式 。 然后是具有流畅接口和外部DSL的内部DSL (根据Fowler的术语)。 这是我最近观看的一个很棒的video:

    • DSL:什么,为什么,如何

    其他答案已经指出了该领域的资源。

  • 反思 。 元编程也是不可分割的forms反映。 在运行时反映程序结构的能力非常强大。 那么了解什么是内省 , 代祷和具体化是很重要的。 恕我直言,reflection允许两大类:1。数据的操作,其结构在编译时是未知的(然后在运行时提供数据的结构,并且程序仍然可以reflection)。 2.强大的编程模式,如动态代理 ,工厂等.Smalltalk是探索reflection的首选, 一切都是reflection性的。 但我认为Ruby也是一个很好的候选者,有一个利用元编程的社区(但我自己对Ruby不太了解)。

    • Smalltalk:一种反思性的语言
    • Magritte:一种支持开发人员和最终用户的元驱动方法

    还有关于反思的丰富文献。

  • 注释 。 注释可以被视为语言reflection能力的一个子集,但我认为它应该属于自己的类别。 我已经回答了注释是什么以及如何使用它们 。 注释是可以在编译时或运行时处理的元数据。 Java通过注释处理器工具 , Pluggable Annotation Processing API和镜像API提供了很好的支持。

  • 字节码或AST转换 。 这可以在编译时或运行时完成。 这在某种程度上是低级方法,但也可以被认为是元编程的一种forms(从某种意义上说,它与非同性语言的宏相同。)

    • 带有Groovy的DSL (最后有一个例子展示了如何使用注释插入自己的AST转换 )。

结论:元编程是程序能够推理自身或修改自身的能力。 就像元堆栈溢出一样,可以询问有关堆栈溢出本身的问题。 元编程不是一种特定的技术,而是概念和技术的集合。

有几件事情属于元编程的范畴。 从您的问题来看,您似乎对宏/ DSL部分更感兴趣。 但一切都是最终相关的,因此元编程的其他方面也是值得关注的。

PS:我知道我提供的大部分链接都不是教程或介绍性文章。 这些是我喜欢的资源,描述了元编程的概念和优点,我认为这更有趣

从您的示例来看,您似乎在谈论特定于域的语言 (DSL),特别是内部DSL。

这里有一大堆关于DSL的书籍(关于像SQL这样的DSL)。

马丁福勒有一本书正在进行中,目前正在线上 。

Ayende写了一本关于DSL的书。

更新 :(以下评论)

元编程是指创建控制其他程序(或其数据)的程序,有时使用DSL。 在这方面,批处理文件和shell脚本可以被认为是元编程,因为它们调用和控制其他程序。

您的示例显示了元编程可用于控制绘图程序的DSL。

我在上面的评论中提到了C ++模板元编程。 因此,让我提供一个使用C ++模板元编程的简短示例。 我知道你用java标记了你的问题,但这可能很有见地。 我希望你能理解C ++代码。


示例演示:

考虑以下递归函数,它生成斐波那契数列 ( 0,1,1,2,3,5,8,13 ……):

 unsigned int fib(unsigned int n) { return n >= 2 ? fib(n-2) + fib(n-1) : n; } 

要从Fibonacci系列中获取项目,您可以调用此函数 – 例如fib(5) – ,它将计算该值并将其返回给您。 到目前为止没什么特别的。


但现在,在C ++中,您可以使用模板重写此代码(有点类似于Java中的generics),因此Fibonacci系列不会在运行时生成,而是在编译时生成

 // fib(n) := fib(n-2) + fib(n-1) template  struct fib // <-- this is the generic version fib { static const unsigned int value = fib::value + fib::value; }; // fib(0) := 0 template <> struct fib<0> // <-- this overrides the generic fib for n = 0 { static const unsigned int value = 0; }; // fib(1) := 1 template <> struct fib<1> // <-- this overrides the generic fib for n = 1 { static const unsigned int value = 1; }; 

要使用此模板从Fibonacci系列中获取项目,只需检索常量值 – 例如fib<5>::value


结论(“这与元编程有什么关系?”):

在模板示例中, C ++编译器在编译时生成Fibonacci系列,而不是在运行时生成程序。 (这很明显,在第一个例子中,你调用一个函数,而在模板示例中,你检索一个常量值。)你得到你的斐波纳契数而不用编写计算它们的函数! 你没有对这个函数进行编程,而是编写了编译器为你做的事情,它没有明确地设计用于…这是非常了不起的。

因此,这是元编程的一种forms:

元编程是编写或编写其他程序(或自身)作为数据的计算机程序,或者在编译时执行部分工作的计算机程序, 否则将在运行时完成

– 来自维基百科关于元编程的文章的定义,重点是我。

(另请注意上述模板示例中的副作用:当您使编译器预先计算您的Fibonacci数时,它们需要存储在某处。程序二进制文件的大小将与包含在表达式中的最高n成比例增加术语fib::value 。从好的方面来说,可以节省运行时的计算时间。)

Tcl最初是作为一种特定领域语言的一种方式,因为它们在复杂性方面越来越需要获得通用编程function。 而且,在您自己的命令中添加它仍然非常容易,因为它仍然是该语言的重要用例。

如果您想要一个与Java集成的实现, Jacl是Java Tcl的一个实现它提供了专注于DSL的脚本,并且还可以访问任何Java对象。

(Metaprogramming正在编写编写程序的程序。有些语言比其他语言做得更多。为了了解一些特定情况,Lisp是一种经常进行大量元编程的语言的例子; C ++倾向于将它转移到模板而不是允许它在运行时;脚本语言都倾向于发现元编程更容易,因为它们的实现编写得更灵活,尽管这只是程度问题..)

好吧,在Java生态系统中,我认为实现迷你语言的最简单方法是使用脚本语言,如Groovy或Ruby (是的,我知道,Ruby不是java生态系统的本地公民)。 两者都提供了相当不错的DSL规范机制,这将使您能够以比Java语言更简单的方式实现这一点:

  • 在Groovy中编写DSL
  • 创建Ruby DSL

然而,有纯Java替代品,但我认为它们将更难实现。

您可以查看eclipse建模项目 ,他们已经获得了对元模型的支持。

关于Metraprogramming的Pluralsight课程可能是一个很好的切入点https://app.pluralsight.com/library/courses/understanding-metaprogramming/table-of-contents