Chinaunix首页 | 论坛 | 博客
  • 博客访问: 380593
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: Java

2015-07-02 18:02:49

Clojure Reader

 

  • 标量类型
  1. 字符串

    a、双引号扩住

    "hello clojure"

    b、 字符串天然支持多行

    "multiline strings

    are very handy"

  2. 布尔值:true 、false
  3. 字符:

    通过反斜线标识

    a、普通字符:\h \e \l \l \o \space

    b、特殊字符:\newline \formfeed \return \backspace \tab

  4. 关键字 keyword -- 关键字是一种命名类型

     

    求值成他们自身,经常被用来当做访问器来获取他们对应的值,如

     

(def person {:name "Sandra Crue" :city "Portland,ME"}) ;定义一个 map

;= #'user/person

(:city person) ; 访问 person 的 :city 属性,关键字 :city 本身就是函数

;= "Portland,ME"

 

关键字始终以冒号 : 开头

 

  1. 如果包含 /,说明有名字空间限定
  2. 如果是两个冒号 :: 表示是当前名字空间
  3. 如果类似 ::alias/kw 表示是某个特定命名空间里面

 

关键字是一种命名类型,可以通过 name 、namespace 函数获取其名字和名字空间:

(name :user/location)

;= "location"

(namespace :location)

;= nil

 

 

  1. 符号 Symbol – 也是一种命名类型

     

    符号也是一种标识符,其值是它所代表的 Clojure 运行时里面的那个值

(average[60 100 400])

;= 160

average 就是一个符号,代表一个名叫 average 的 var 所指向的函数。

 

  1. 数字
  2. 正则表达式

    Clojure 里面把以 # 开头的字符串当做正则表达式:

     

(class #"(p|h)ail")

;= java.util.regex.Pattern

 

(re-seq #"(...) (...)" "foo bar")

;= (["foo bar" "foo" "bar"])

 

相关函数:re-seq、re-find、re-matches 以及 clojure.string 命名空间中定义的其他方法。

 

  • 注释
  1. 以分号开头的单行注释
  2. 形式(form)级别的注释 #_ 宏,告诉 reader 忽略下一个 Clojure 形式。

(read-string "(+ 1 2 #_(* 2 ) 8)")

;= (+ 1 2 8)

 

  1. 使用 comment 宏的注释

(when true

(comment (println "hello")))

;= nil

跟 #_ 不同的是,comment 不会被忽略,只是返回 nil。

 

  • 空格和逗号: 在 reader 眼里,逗号就是空格,写不写逗号看个人喜好。
  • 集合字面量
  1. list        '(a b :name 12.5)
  2. vector        ['a 'b :name 12.5]
  3. map        {:name "Chas" :age 31}
  4. set        #{1 2 3}

 

单引号表示获取 Symbol 本身,而非其代表值。

Clojure 中使用列表来表示函数调用,因此通过添加一个单引号可阻止求值。

set 类似于枚举类型

 

 

  • reader 的一些其他语法糖
  1. 单引号可以阻止形式求值: (quote (* 1 2)) 同 '(* 1 2) 等价
  2. #() 语法可以定义一个匿名函数
  3. #var 可以阻止 var 求值,得到其本身
  4. 对 ref 可以通过 @ 来解引用,@ref
  5. 三个特殊的语法: ` ~ 以及~@
  6. 除两种跟 Java 互操作的形式,还有其他一些语法糖
  7. 所有的数据结构和引用类型都支持元数据,通过 ^ 语法可以给一个值添加元数据

     

命名空间

 

命名空间是 Clojure 最基本的代码模块组件。所有的 Clojure 代码都是在一个命名空间中被定义和求值的。命名空间可以看成 Ruby 和Python 中的 module,Java 中的package。从本质上来说,命名空间是符号跟 vars 或者引入的 Java 类的一个映射关系。Clojure REPL 启动时默认的命名空间始终是 user。

Var 是 Clojure 中的一种引用类型,它是一种可修改的内存地址,从而可以保存任何值。在 var 被定义的命名空间里,var 和一个符号相关联,然后我们就可以通过这个符号来使用这个 var 。var 是用特殊形式 def 来定义的,def 只作用于当前命名空间。

个人备注:var 都是通过 def 来定义的,然后必定同某个 Symbol 关联,所以标量类型里面只有 Symbol 没有 var ,Symbol 就是用来引用 var 的。

(def x "hello") ;定义了一个 var ,同 Symbol x 关联

;= #'user/x

*ns* ;查看当前名字空间

;= #<Namespace user>

(ns foo) ;创建一个新的名字空间,并切换过去

*ns*

;= #<Namespace foo>

user/x

;= "hello"

x

;= Unable to resolve symbol: x in the contex ....

 

当前的命名空间始终会绑定到 *ns*

Clojure 中的命名空间还负责维护符号和被引入 (import) 的Java 类之间的映射。

因为 java.lang 中的类默认被引入到每个 Clojure 命名空间中,所以可以直接访问这些 java 类。

同时,每个命名空间都会默认引入 Clojure 的核心库 clojure.core 中的所有 var,如 filter 函数等。

String

;= java.lang.String

Integer

;= java.lang.Integer

java.util.List

;= java.util.List

java.net.Socket

;= java.net.Socket

 

符号求值

 

分析下 average 函数

(defn average [numbers]

(/ (apply + numbers) (count numbers) ) )

 

根据"同向性",这些代码其实就是 Clojure 的数据结构。

  • / 、apply、+ 和 count 都求值成 clojure.core 命名空间中的函数
  • numbers 在参数数组中时,是定义这个函数的唯一参数
  • numbers 在函数体中被引用时,求值成这个符号 (Symbol)的值。

 

看下 (average [60 80 100 400] ) 的运算过程。

这里符号 average 求值成它的 #'average,也就是当前命名空间中定义的那个函数。

传给它一个 vector,函数内部通过 numbers 来引用它。函数返回值传给 REPL ,REPL 将其输出到 stdout。

个人备注:解释下 #'average;我们知道 average (Symbol) -> var (内存指针) -> 函数体

因此 ' 阻止 average求值,得到一个 var ,再用 # 阻止var 求值,就得到了函数体。函数体放入 ( ) 就可以进行运算。

 

 

 

 

阅读(1020) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~