首页 > 代码库 > clojure 常见问题的解释

clojure 常见问题的解释

以下内容都出现在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。也就是你我作为程序员最熟悉的对象。

clojure 常见问题的解释