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

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

(require quoted-namespace-symbol)
当你使用require加载了一个名为clojure.java.io的库时,Clojure会在CLASSPATH中查找名为clojure/java/io.clj的文件。试试看。

user=> (require 'clojure.java.io)
-> nil

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

user=> (require 'examples.introduction)
-> nil

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

(take 10 examples.introduction/fibs)
-> (0 1 1 2 3 5 8 13 21 34)

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

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

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

(require 'examples.introduction)
-> nil
(take 10 examples.introduction/fibs)
-> (0 1 1 2 3 5 8 13 21 34)

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

(refer quoted-namespace-symbol)

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

(refer 'examples.introduction)
-> nil
(take 10 fibs)
-> (0 1 1 2 3 5 8 13 21 34)

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

(use quoted-namespace-symbol)

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

(use 'examples.introduction)
-> nil
(take 10 fibs)
-> (0 1 1 2 3 5 8 13 21 34)

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

(use :reload 'examples.introduction)
-> nil

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

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

(doc name)

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

user=> (doc str)
-------------------------
clojure.core/str
([] [x] [x & ys])
With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
one arg, returns the concatenation of the str values of the args.

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

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

src/examples/introduction.clj
(defn hello
 "Writes hello message to out. Calls you by username"
 [username]
 (println (str "Hello, " username)))

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

(find-doc s)

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

user=> (find-doc "reduce")
-------------------------
clojure/areduce
([a idx ret init expr])
Macro
... details elided ...
-------------------------
clojure/reduce
([f coll] [f val coll])
... details elided ...

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函数的源码。

(clojure.repl/source a-symbol)

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

(use 'clojure.repl)
(source identity)
-> (defn identity
   "Returns its argument."
   {:added "1.0"
   :static true}
   [x] x)

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

(ancestors (class [1 2 3]))
-> #{clojure.lang.ILookup clojure.lang.Sequential
   java.lang.Object clojure.lang.Indexed
   java.lang.Iterable clojure.lang.IObj
   clojure.lang.IPersistentCollection
   clojure.lang.IPersistentVector clojure.lang.AFn
   java.lang.Comparable java.util.RandomAccess
   clojure.lang.Associative
   clojure.lang.APersistentVector clojure.lang.Counted
   clojure.lang.Reversible clojure.lang.IPersistentStack
   java.util.List clojure.lang.IEditableCollection
   clojure.lang.IFn clojure.lang.Seqable
   java.util.Collection java.util.concurrent.Callable
   clojure.lang.IMeta java.io.Serializable java.lang.Runnable}

http://clojure.github.com/clojure有完整的Clojure API在线文档。右侧栏按照名称对所有的函数和宏建立了链接,左侧栏则链接至一系列描述Clojure的特性的文章。

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

时间: 2024-10-26 10:18:17

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

《Clojure程序设计》——第1章,第1.4节小结

1.4 小结 Clojure程序设计 你刚才完成了一次旋风般的Clojure之旅.你已经看到了Clojure极富表现力的语法,了解了Clojure的Lisp之道,还见到了从Clojure中调用Java代码有多么容易. 你已经有了一份运行在你自己环境中的Clojure,此外你还在REPL中编写了几款短小的程序,用来演示函数式编程和解决状态问题的引用模型.现在是时候去探索一下整个语言了.

《Clojure程序设计》——第1章,第1.2节Clojure编程快速入门

1.2 Clojure编程快速入门 Clojure程序设计 要运行Clojure及本书的示例代码,你需要两件东西. Java运行时.请下载1并安装Java 5或是更高版本.Java 6具有显著的性能提升和更好的异常报告,如果可能就尽量选它吧. Leiningen2.Leiningen是一个用于管理依赖项的工具,并且可以基于你的代码启动各种任务.在Clojure世界中,它是处理这项工作最常用的工具了. 你将会使用Leiningen来安装Clojure和本书所有示例代码的依赖项.如果你已经安装了Le

《Clojure程序设计》——导读

目 录 第1章 启航 1.1 为什么是Clojure 1.2节Clojure编程快速入门1.3 探索Clojure的程序库 1.4 小结 第2章 探索Clojure 第3章 一切皆序列 第4章 函数式编程 第5章 状态 第6章 协议和数据类型 第7章 宏 第8章 多重方法 第9章 极尽Java之所能 第10章 搭建应用 附录 编辑器 参考书目

键盘输入-关于windows程序设计第六章的最后一个范例……

问题描述 关于windows程序设计第六章的最后一个范例-- 在第六章(讲键盘)的里面最后有一个typer程序的代码,我能看懂它们但有些不理解为什么作者要这么做------在此贴上消息处理部分的代码恳请了解API的人帮忙解答一下--这里刚入门现在已经完全昏了 LRESULT CALLBACK WndProc (HWND hwnd UINT message WPARAM wParam LPARAM lParam){ static DWORD dwCharSet = DEFAULT_CHARSET

java-Java语言程序设计第4章编程练习题1求问

问题描述 Java语言程序设计第4章编程练习题1求问 import java.util.Scanner; public class Practice { public static void main(String[] args) { Scanner input = new Scanner(System.in); int data = 0; int positive = 0; int negative = 0; int sum = 0; int count = 0; System.out.pri

《敏捷可执行需求说明 Scrum提炼及实现技术》—— 第3章 使用短周期反馈环探索干系人的“愿求”

第3章 使用短周期反馈环探索干系人的"愿求" 上一章解决了当软件需求存在许多不确定性的时候,一个健康的团队.有着共同的愿景.有意义的共同目标和一组高级别的特征都很有必要.它们组成了引领团队前进的坚实基础.确定产品的愿景只是一个更庞大的创建成功的软件产品的流程的第一步.为了使软件产品成为现实,下一步就是学会如何应对干系人们持续变化的需求.首先,需要通过短周期反馈环的方式运用试错法,其次,你必须专注于干系人的愿望和需求.这就是本章的意图.

《C++程序设计教程(第3版)》——第1章,第1节计算机语言与程序

第1章 C++概述1.1 计算机语言与程序 人类语言是人与人之间交流信息的工具,而计算机语言是人与计算机之间交流信息的工具.用计算机解决问题时,人们必须首先将解决问题的方法和步骤按照一定的规则和序列用计算机语言描述出来,形成计算机程序,然后让计算机自动执行程序,完成相应功能,解决指定的问题.下面先介绍计算机语言与程序经历的3个发展阶段. 1.1.1 机器语言与程序 机器语言是第一代计算机语言.任何信息在计算机内部都是采用二进制代码表示的,指挥计算机完成一个基本操作的指令(称为机器指令)也是由二进

《C++程序设计教程(第3版)》——第3章,第1节传统的输入输出函数实现方法

第3章 简单的输入输出通过前面的章节我们了解了组成一个程序的基本数据元素,下面我们就可以在数据元素的基础上开始学习编写简单的C++程序了.本章主要介绍计算机程序设计的基础:在C++程序中实现数据输入输出的基本方法.由于C++中数据输入输出的实现机制与后续章节中的面向对象重载等技术有着密切的联系,因此在没有系统地学习面向对象设计思想之前无法对此进行详细的介绍.因此,本章只对C++中数据输入输出的使用方法做简单的介绍.有关C++编译器提供的完整的输入输出流体系结构的实现原理,将在第14章中进行详细的

《C++程序设计教程(第3版)》——第3章,第2节cout输出流

3.2 cout输出流计算机的输入输出分为两大类:标准输入输出和文件输入输出.键盘是计算机的默认标准输入设备,显示器是计算机的默认标准输出设备,所以在键盘和显示器上的输入输出称为标准输入输出.数据在磁盘上是以文件为单位存放的,所以磁盘数据的输入输出称为文件输入输出.由于在一个系统中,键盘和显示器一般都只有一个,从键盘获取数据,将结果显示到显示器上,所以操作起来比较简单.而在磁盘访问中,往往需要同时对多个文件进行操作,所以操作起来比较复杂.本章是C++学习的基础,所以只讨论简单的标准输入输出的操作