本节书摘来自异步社区《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函数给占用了!尽管从语法角度,参数可以和其他函数重名,但这是一种糟糕的风格:参数会遮蔽函数,当它们同处一室时,后者将会失效。所以,千万不要把你的引用叫做ref、把代理叫做agent,或者把数量叫做count。这些名称代表的是函数。
clojure自己的源码中,很多都是用clojure本身写成的,阅读这些源码极具启发性。你可以使用repl库的source函数来查阅某个clojure函数的源码。
查阅一下简单的identity函数源码看看。
当然,你也可以使用java的反射api。用诸如class、ancestors和instance?这样的方法,来反射其底层的java对象模型。例如,clojure的容器同样也是java容器。
1新开启一个repl,能防止你之前输入的代码,与本书示例代码中的同名函数之间产生名字冲突。如同你将在“命名空间”中看到的那样,在实际开发中这不会成为问题。
2 doc实际上是一个clojure的宏。