Almost Lisp
离Lisp越来越近
Let's forget about the operator business for the moment and try to expand our
horizons beyond the constraints of Ant's design. I mentioned earlier that Ant
can be extended by writing conventional Java classes. Ant interpreter then
attempts to match XML elements to appropriately named Java classes and if the
match is found the task is executed. An interesting question begs to be
asked. Why not extend Ant in Ant itself? After all, core tasks contain a lot of
conventional programming language constructs ('if' being a perfect example). If
Ant provided constructs to develop tasks in Ant itself we'd reach a higher
degree of portability. We'd be dependent on a core set of tasks (a standard
library, if you will) and we wouldn't care if Java runtime is present: the core
set could be implemented in anything. The rest of the tasks would be built on
top of the core using Ant-XML itself. Ant would then become a generic,
extensible, XML-based programming language. Consider the possibilities:
我们先把算符的事情放一放, 考虑一下Ant设计局限之外的东西。我早先说过, Ant可以通
过写Java类来扩展。Ant解析器会根据名字来匹配XML元素和Java类, 一旦找到匹配, 就执
行相应任务。为什么不用Ant自己来扩展Ant呢? 毕竟核心任务要包含很多传统语言的结构
(例如"if"), 如果Ant自身就能提供构造任务的能力(而不是依赖java类), 我们就可以得
到更高的移植性。我们将会依赖一组核心任务(如果你原意, 也不妨把它称作标准库), 而
不用管有没有Java 环境了。这组核心任务可以用任何方式来实现, 而其他任务建筑在这
组核心任务之上, 那样的话, Ant就会成为通用的, 可扩展的, 基于XML的编程语言。考虑
下面这种代码的可能性:
<task name="Test">
<echo message="Hello World" />
</task>
<Test />
<echo message="Hello World" />
</task>
<Test />
If ant supported the "task" construct, the example above would print "Hello
World!". In fact, we could write a "task" task in Java and make Ant able to
extend itself using Ant-XML! Ant would then be able to build more complicated
primitives on top of simple ones, just like any other programming language! This
is an example of "XML" based programming language we were talking about in the
beginning of this tutorial. Not very useful (can you tell why?) but pretty damn
cool.
如果XML支持"task"的创建, 上面这段代码就会输出"Hello World!". 实际上, 我们可
以用Java写个"task"任务, 然后用Ant-XML来扩展它。Ant可以在简单原语的基础上写出更复
杂的原语, 就像其他编程语言常用的作法一样。这也就是我们一开始提到的基于XML的编程语
言。这样做用处不大(你知道为甚么吗?), 但是真的很酷。
By the way, take a look at our 'Test' task once again. Congratulations. You're
looking at Lisp code. What on Earth am I talking about? It doesn't look anything
like Lisp? Don't worry, we'll fix that in a bit. Confused? Good. Let's clear it
all up!
再看一回我们刚才说的Task任务。祝贺你呀, 你在看Lisp代码!!! 我说什么? 一点都不像
Lisp吗? 没关系, 我们再给它收拾一下。
A Better XML
比XML更好
I mentioned in the previous section that self-extending Ant wouldn't be very
useful. The reason for that is XML's verbosity. It's not too bad for data files
but the moment you try writing reasonably complex code the amount of typing you
have to do quickly starts to get in the way and progresses to becoming unusable
for any real project. Have you ever tried writing Ant build scripts? I have, and
once they get complex enough having to do it in XML becomes really
annoying. Imagine having to type almost everything in Java twice because you
have to close every element. Wouldn't that drive you nuts?
前面一节说过, Ant自我扩展没什么大用, 原因在于XML很烦琐。对于数据来说, 这个问题
还不太大, 但如果代码很烦琐的话, 光是打字上的麻烦就足以抵消它的好处。你写过Ant
的脚本吗? 我写过, 当脚本达到一定复杂度的时候, XML非常让人厌烦。想想看吧, 为了
写结束标签, 每个词都得打两遍, 不发疯算好的!
The solution to this problem involves using a less verbose alternative to
XML. Remember, XML is just a format for representing hierarchical data. We don't
have to use XML's angle brackets to serialize trees. We could come up with many
other formats. One such format (incidentally, the one Lisp uses) is called an
s-expression. S-expressions accomplish the same goals as XML. They're just a lot
less verbose, which makes them much better suited for typing code. I will
explain s-expressions in a little while, but before I do I have to clear up a
few things about XML. Let's consider our XML example for copying files:
为了解决这个问题, 我们应当简化写法。须知, XML仅仅是一种表达层次化数据的方式。
我们并不是一定要使用尖括号才能得到树的序列化结果。我们完全可以采用其他的格式。
其中的一种(刚好就是Lisp所采用的)格式, 叫做s表达式。s表达式要做的和XML一样, 但
它的好处是写法更简单, 简单的写法更适合代码输入。后面我会详细讲s表达式。这之前
我要清理一下XML的东西。考虑一下关于拷贝文件的例子:
<copy toDir="../new/dir">
<fileset dir="src_dir">
</copy>
<fileset dir="src_dir">
</copy>
Think of what the parse tree of this snippet would look like in memory. We'd
have a 'copy' node that contains a fileset node. But what about attributes? How
do they fit into our picture? If you've ever used XML to describe data and
wondered whether you should use an element or an attribute, you're not
alone. Nobody can really figure this out and doing it right tends to be black
magic rather than science. The reason for that is that attributes are really
subsets of elements. Anything attributes can do, elements can do as well. The
reason attributes were introduced is to curb XML's verbosity. Take a look at
another version of our 'copy' snippet:
想想看在内存里面, 这段代码的解析树在内存会是什么样子? 会有一个"copy"节点, 其下
有一个 "fileset"节点, 但是属性在哪里呢? 它怎样表达呢? 如果你以前用过XML, 并且
弄不清楚该用元素还是该用属性, 你不用感到孤单, 别人一样糊涂着呢。没人真的搞得清
楚。这个选择与其说是基于技术的理由, 还不如说是闭着眼瞎摸。从概念上来讲, 属性也
是一种元素, 任何属性能做的, 元素一样做得到。XML引入属性的理由, 其实就是为了让
XML写法不那么冗长。比如我们看个例子:
<copy>
<toDir>../new/dir</toDir>
<fileset>
<dir>src_dir</dir>
</fileset>
</copy>
<toDir>../new/dir</toDir>
<fileset>
<dir>src_dir</dir>
</fileset>
</copy>
The two snippets hold exactly the same information. However, we use attributes
to avoid typing the same thing more than once. Imagine if attributes weren't
part of XML specification. Writing anything in XML would drive us nuts!
两下比较, 内容的信息量完全一样, 用属性可以减少打字数量。如果XML没有属性的话,
光是打字就够把人搞疯掉。
Now that we got attributes out of the way, let's look at s-expressions. The
reason we took this detour is that s-expressions do not have attributes. Because
they're a lot less verbose, attributes are simply unnecessary. This is one thing
we need to keep in mind when transforming XML to s-expressions. Let's take a
look at an example. We could translate above snippet to s-expressions like this:
说完了属性的问题, 我们再来看一看s表达式。之所以绕这么个弯, 是因为s表达式没有属
性的概念。因为s表达式非常简练, 根本没有必要引入属性。我们在把XML转换成s表达式
的时候, 心里应该记住这一点。看个例子, 上面的代码译成s表达式是这样的:
(copy
(todir "../new/dir")
(fileset (dir "src_dir")))
(todir "../new/dir")
(fileset (dir "src_dir")))
Take a good look at this representation. What's different? Angle brackets seem
to be replaced by parentheses. Instead of enclosing each element into a pair of
parentheses and then closing each element with a "(/element)" we simply skip the
second parenthesis in "(element" and proceed. The element is then closed like
this: ")". That's it! The translation is natural and very simple. It's also a
lot easier to type. Do parentheses blind first time users? Maybe, but now that
we're understand the reasoning behind them they're a lot easier to handle. At
the very least they're better than arthritis inducing verbosity of XML. After
you get used to s-expressions writing code in them is not only doable but very
pleasant. And they provide all the benefits of writing code in XML (many of
which we're yet to explore). Let's take a look at our 'task' code in something
that looks a lot more like lisp:
仔细看看这个例子, 差别在哪里? 尖括号改成了圆括号, 每个元素原来是有一对括号标记
包围的, 现在取消了后一个(就是带斜杠的那个)括号标记。表示元素的结束只需要一个")"
就可以了。不错, 差别就是这些。这两种表达方式的转换, 非常自然, 也非常简单。s表
达式打起字来, 也省事得多。第一次看s表达式(Lisp)时, 括号很烦人是吧? 现在我们明
白了背后的道理, 一下子就变得容易多了。至少, 比XML要好的多。用s表达式写代码, 不
单是实用, 而且也很让人愉快。s表达式具有XML的一切好处, 这些好处是我们刚刚探讨过
的。现在我们看看更加Lisp风格的task例子:
(task (name "Test")
(echo (message "Hello World!")))
(Test)
(echo (message "Hello World!")))
(Test)
S-expressions are called lists in Lisp lingo. Consider our 'task' element
above. If we rewrite it without a line break and with comas instead of spaces
it's starting to look surprisingly like a list of elements and other lists (the
formatting is added to make it easier to see nested lists):
用Lisp的行话来讲, s表达式称为表(list)。对于上面的例子, 如果我们写的时候不加换
行, 用逗号来代替空格, 那么这个表达式看起来就非常像一个元素列表, 其中又嵌套着其
他标记。
(task, (name, "test"), (echo, (message, "Hello World!")))
We could do the same with XML. Of course the line above isn't really a list,
it's a tree, just like its XML-alternative. Don't let references to lists
confuse you, it's just that lists that contain other lists and trees are
effectively the same thing. Lisp may stand for List Processing, but it's really
tree processing - no different than processing XML nodes.
XML自然也可以用这样的风格来写。当然上面这句并不是一般意义上的元素表。它实际上
是一个树。这和XML的作用是一样的。称它为列表, 希望你不会感到迷惑, 因为嵌套表和
树实际上是一码事。Lisp的字面意思就是表处理(list processing), 其实也可以称为树
处理, 这和处理XML节点没有什么不同。
Whew. After much rambling we finally got to something that looks like Lisp (and
is Lisp, really). By now the mysterious Lisp parentheses as well as some claims
made by Lisp advocates should become more clear. But we still have a lot of
ground to cover. Ready? Let's move on!
经受这一番折磨以后, 现在我们终于相当接近Lisp了, Lisp的括号的神秘本质(就像许多
Lisp狂热分子认为的)逐渐显现出来。现在我们继续研究其他内容。