天天看点

《Clojure程序设计》——第1章,第1.3节探索Clojure的程序库

本节书摘来自异步社区《clojure程序设计》一书中的第1章,第1.3节探索clojure的程序库,作者 【美】stuart halloway , aaron bedra,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.3 探索clojure的程序库

clojure程序设计

clojure代码通常都被打包在程序库中。每个clojure库都属于某个命名空间,这与java的包非常类似。你可以通过require来加载一个clojure库。

(require quoted-namespace-symbol)

当你使用require加载了一个名为clojure.java.io的库时,clojure会在classpath中查找名为clojure/java/io.clj的文件。试试看。

起头的单引号(')是必不可少的。它表示对库名的引用(关于引用的内容参见2.2节“读取器宏”)。返回nil表示库加载成功。如果你想测试一下,可以加载本章的示例代码examples.introduction。

examples.introduction库中包含了一个fibonacci数列的实现,这是函数式编程语言传统的“hello world”程序。在4.2节“怎样偷个懒”中,我们会探索关于fibonacci数列的更多细节。现在,你只要确保能够执行这个示例函数fibs即可。你可以在repl中输入下面的代码行以获取前10个fibonacci数。

如果你看到的前10个fibonacci数与此处列出的相同,说明你已经成功安装了本书的示例。

本书的所有示例都进行了单元测试,测试代码位于examples/test目录。本书并未明确包含这些示例的测试本身,但你会发现它们可以作为非常有用的参考。你也可以自己执行lein test命令来运行这些单元测试。

1.3.1 require和use

当你用require加载了一个clojure程序库,就需要使用命名空间限定的名称来引用这个库里面的内容。你必须这么写:examples.introduction/fibs,而不是简单地写下fibs了事。下面,确保你新启动了一个repl1,然后试着输入。

总要使用完全限定名实在是太啰嗦了。你可以对命名空间使用refer,将其下的所有名称映射到你的当前命名空间中。

对examples.introduction调用refer,然后确认你能否直接调用fibs了。

为方便起见,clojure的use函数把require和refer合并成了一个步骤。

在新启动的repl中,你应该能够顺利执行下列代码。

在使用本书的示例代码期间,你可以在调用require或use时,使用:reload标记用来强制重新加载一个程序库。

如果你进行了一些修改,想看看结果如何,但又不想重新启动repl,那么:reload标记就能帮上大忙。

1.3.2 查找文档

通常情况下,你都可以在repl中找到你需要的文档。最基本的辅助函数2是doc。

试试使用doc来打印str函数的文档。

doc输出的第一行包含了目标函数的全限定名称。接下来的一行包含了可能的参数列表,这是直接从代码中生成的。“参数命名惯例”中有一些常用的参数名称,及它们的用途解释。最后,剩下的那几行是这个函数的文档字符串(doc string),当然,如果在函数定义中包含了它们的话。

你可以为自己的函数添加文档字符串,只要将它放置在紧接着函数名的位置即可。

有时你想要查阅文档时,不清楚目标的确切名称。find-doc会用你传入的正则表达式或是字符串,找出所有那些调用doc函数得到的输出能与之匹配的东西。

试试用find-doc检索一下clojure是如何进行reduce的。

reduce用于对clojure容器进行归纳,第3.2.4小节“序列转换”中讨论了该话题。areduce则作用于java数组,第9.4.2小节“使用java容器”中有关于它的讨论。

参数命名惯例

reduce和areduce的文档字符串展示了几个简练的参数名称。表1-1列出了一些约定的参数名,以及通常情况下应该如何使用它们。

表1-1 参数名

《Clojure程序设计》——第1章,第1.3节探索Clojure的程序库

这些名称看起来似乎过于简练了,但采用它们有一个很好的理由:“好名称”往往已经被clojure函数给占用了!尽管从语法角度,参数可以和其他函数重名,但这是一种糟糕的风格:参数会遮蔽函数,当它们同处一室时,后者将会失效。所以,千万不要把你的引用叫做ref、把代理叫做agent,或者把数量叫做count。这些名称代表的是函数。

clojure自己的源码中,很多都是用clojure本身写成的,阅读这些源码极具启发性。你可以使用repl库的source函数来查阅某个clojure函数的源码。

查阅一下简单的identity函数源码看看。

当然,你也可以使用java的反射api。用诸如class、ancestors和instance?这样的方法,来反射其底层的java对象模型。例如,clojure的容器同样也是java容器。

1新开启一个repl,能防止你之前输入的代码,与本书示例代码中的同名函数之间产生名字冲突。如同你将在“命名空间”中看到的那样,在实际开发中这不会成为问题。

2 doc实际上是一个clojure的宏。