以下内容都出现在clojure官网的文档中,此文不过是将常见的疑问总结一下而已。
1、namespace,lib以及代码文件的约定。看下面的代码:
(ns com.my-company.clojure.examples.my-utils (:import java.util.Date) (:use [clojure.contrib.def :only (defvar-)]) (:require [clojure.contrib.shell-out :as shell]))
lib表现为java的resource。如果写过java代码,应该熟悉下面的目录结构:
myprj ----src ----resources --------com ------------my_company ----------------clojure --------------------examples ------------------------my_utils.clj
这就是对应关系,需要注意的是dash(-)变成underscore(_)。后缀名是clj。
ns是一个macro,这意味着:
由macro对未经eval的form进行处理,返回的结果进行eval。
正因为如此,虽然在ns中的这些自由symbol未经绑定而不会出错。如果你在repl中使用:
(import java.util.Date) (use [clojure.contrib.def :only (defvar-)]) (require [clojure.contrib.shell-out :as shell])
就会出错,因为import,use,require都是函数,java.util.Date这个symbol当然无法解析了。
2、list,(1 2 3)
这个list一定困惑了一些人。因为clojure给了它额外的意义。
(map #(+ 1 %) ()); 这个可以 (map #(+ 1 %) (1 2 3));这个出错
An empty list () evaluates to an empty list.
Non-empty Lists are considered calls to either special forms, macros, or functions. A call has the form (operator operands*).
一个空的list,求值结果是空的list。
不空的list,就认为是呼叫,特殊form,宏,函数。1当然不是三者之一,所以出错。下面的不会出错。
(map #(+ 1 %) '(1 2 3));前面加了一个单引号或者那个反向的单引号 (map #(+ 1 %) (list 1 2 3));
在回到刚才的话题,为什么这样use就行了呢?
(import 'java.util.Date)
import既然是函数,那总有一个地方去解析这个java.util.Date?这里牵涉到你对于symbol的理解。这个有点难以描述,我只好用例子的方式来说明。
你在代码中看到一个x。这个x有可能是一个var,或者ref,或者agent,或者atom,不去管它。colure如何描述这个x本身,它是x而不是y。而上面提到的都是var,ref等等都是它所代表的东西。
(x 1 2);这个x有可能是函数,宏 @x;这个x可能是ref,agent,atom,promise,future等等 (+ x 1);x应该是一个var
为了方便理解,这里同时介绍一下clojure的reader的Dispatch,当reader遇到#开头时,它会以另一种方式去理解后面的内容。
(def x 5) #'x
上面的代码中,x是一个var,它的值是5,这个var也是一个对象,那么如果得到这个对象呢?#'x得到的symbol x代表的var这个对象,reader在常规解析中x就解析成它的值。
[1 x] ----> [1 5]
掌握了这一点,那么对于symbol本身的获取就是:
'x 注意这时的x可以是可以没有初始化的,可以不代表任何东西,'x,就是我我是符号x。
symbol可以转换成string。
(name 'x)就得到"x".
上面的代码意思就是:我的名字叫x。
所以上面的use 函数,接受转义的参数后,通过各种转化,最终交付给reader看到的就是string。也就是你我作为程序员最熟悉的对象。