一个缺乏语义元素组织管理框架的编程语言会显得杂乱无章,并且会导致各种莫名的问题,当然而今这样的语言似乎很难生存。
Clojure也有自己的命名空间,不同于Java等其他语言的是,clojure在逻辑上的组织并非由目录的自然组织方式决定(大多数时候人们还是习惯这么做的,比较直观)。Java将方法组织在一个类中,而类又被组织在包中。Clojure则将包括Vars、Refs、Atoms、Agents、functions、mocros、甚至namespace自己都组织到以symbols命名的namespace中。
Symbols被用于给函数、宏和绑定变量分配名称。Symbols被划分到namespace中。当前总有一个默认的namespace,初步设置在”user”中,并且这个值是保存在全局变量*ns*中的。有两种方式可以改变默认的namespace。in-ns仅仅是改变它,而ns宏除了改变它还会将 clojure.core namespace中的所有symbols在新的namespace中设为可用状态(通过refer)。有关ns的宏的更多特性随后进行阐述。
user名称空间,可以访问clojure.core名称空间中所有的符号。同样通过使用ns宏所声明的任何名称空间也能够访问clojure.core名称空间中所有的符号。
要访问元素如果不在默认的名称空间中,那么这些元素必须加上namespace限定符。只需在namespace名称后加上一个斜杠再加上name就可以。例如clojure.string库中定义了join函数。它可以将一个序列中的所有项的字符串表示形式加上一个给定的分隔符连接成一个字符串。该函数的namespace限定符(namespace-qualified)为clojure.string/join。
函数require用于载入Clojure库。它接受一到多个namespace的引用名称。例如:
(require 'clojure.string)
(require namespace-qualified)只会载入这个类库。这里面的名字还必须是一个全限定的包名, 包名之间用.分割。注意,clojure里面名字空间和方法名之间的分隔符是/而不是java里面使用的. 。例如:
(clojure.string/join "$" [1 2 3]) ; -> "1$2$3"
alias
函数给一个名字空间指定一个别名以减少我们打字工作。当然这个别名的定义只在当前的名字空间里面有效。例如:
(alias 'su 'clojure.string) (su/join "$" [1 2 3]) ; -> "1$2$3"
refer
函数使得指定的名字空间里面的所有symbols在当前名字空间里面可以访问(不用使用全限定名字)如果当前名字空间有那个名字空间一样的名字,会抛出一个异常。通过指定名字空间的可以解决这一问题。 例如:
(refer 'clojure.string)
这时之前得代码可以写成如下形式:
(join "$" [1 2 3]) ; -> "1$2$3"
require和refer经常被联合使用,因此有了其便捷版本use,如下所示:
(use 'clojure.string)
之前提到过的ns宏可以改变默认的namespace。比较典型的用法是至于一个clojure源文件的头部。它支持:require\:use\:import(用于引入java类),用于替代他们所代指的函数形式。注意下面的示例中,:as是用于创建一个namespace的别名。:only则用于仅载入一个Clojure库的某部分。
(ns com.ociweb.demo (:require [clojure.string :as su]) ; assumes this dependency: [org.clojure/math.numeric-tower "0.0.1"] (:use [clojure.math.numeric-tower :only (gcd, sqrt)]) (:import (java.text NumberFormat) (javax.swing JFrame JLabel))) (println (su/join "$" [1 2 3])) ; -> 1$2$3 (println (gcd 27 72)) ; -> 9 (println (sqrt 5)) ; -> 2.23606797749979 (println (.format (NumberFormat/getInstance) Math/PI)) ; -> 3.142 ; See the screenshot that follows this code. (doto (JFrame. "Hello") (.add (JLabel. "Hello, World!")) (.pack) (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (.setVisible true))
create-ns创建一个新的namespace,但并没有设置为当前默认的。def函数则在默认的namespace中创建一个带有一个可选初值的symbol。intern函数则在给定的namespace中定义一个symbol(如果该symbol不存在的话),也可以为其设置一个初值。注意intern中的symbol的名称需要一个引号,def的是不需要的。这是因为def是一个不需要求值它的所有参数的特殊形式,而intern是一个函数,意味着它需要对它所有的参数求值。
(def foo 1)
(create-ns 'com.ociweb.demo)
(intern 'com.ociweb.demo 'foo 2)
(println (+ foo com.ociweb.demo/foo)) ; -> 3
ns-interns函数返回一个包含所有在给定并且已经载入namespace中定义的symbols的map。map的键是所有的Symbol对象的名称,而值则是一个个代表函数、宏或者绑定变量的Var对象。
ns-interns 'clojure.math.numeric-tower)
all-ns函数返回当前载入的namespace序列。当一个clojure程序在运行时,默认载入了以下namespace:clojure.core, clojure.main, clojure.set,clojure.xml, clojure.zip and user。
在REPL环境中默认载入的namespace还有clojure.repl和 clojure.java.javadoc。namespace函数返回给定symbol或者关键词的namespace。这里没有讨论到的和namespace有关的函数包括ns-aliases, ns-imports, ns-map, ns-name, ns-publics, ns-refers, ns-unalias, ns-unmap and remove-ns