标签:style http io os ar java for strong sp
一、clojure是什么?
clojure官网上有详细的说明,虽然能够理解英文,但脑子里面总是找不到对应的中文来翻译,与其翻译的不伦不类,不如不翻译了。这里我用一个类比来初略的说明,不能保证原意的准确度,但你入门之后,自然会发现其中的差异,这种不准确不会损害你对该语言的理解和深入。
xx.json -> Gson | Jackson -> Java 数据结构
xx.clj -> core/read -> Clojure 数据结构
上面的示例中,xx.clj是一个clojure源程序代码文件,它相当于一个json文件,这就有别于其它类型的语言了,clojure程序就是数据结构。
关于Clojure是什么,就说这最重要的一点。
二、clojure处理问题,就像unix对于“文件”,趋向于一种统一的,高度概括的模式
如果你尝试用bash写代码,会发现所有的工具都采用类似的方法解决问题,比如sed,你面对的是“行”,你的代码看起来只处理了一行,其实是全部的行。
sed -i "s/java/clojure/" /etc/xxx/default.conf
虽然这个类比有点牵强,但是如果你不注意这一点,就用不好sed。同样,对于Clojure,你需要有clojure的思维。Clojure通过Sequence抽象绝大部分的问题,将处理许多对象变成处理一个对象。你的绝大部分思考是如何获得序列,然后转换,转化,直到你想要的结果。
顺着这个思路,我们来看如何解决刚才的sed解决的问题。这里先说说Clojure和Java的关系,Clojure在jvm中运行,可以调用几乎所有的java库,可以这样理解,clojure是老板,java是具有超人能力的打工仔,java能做的clojure都能做。为了代码看起来更像clojure,clojure将一些非常常用的java功能包裹了一下,比如io,当然不是全部,是最常用的。(这个编辑器没有clojure语法)
(with-open [rdr (io/reader (io/file "/etc/xxx/default.conf"))] (这里开始处理问题))
注意,不要去思考如何循环(如果你习惯其它程序语言),思考如何得到序列。
(with-open [rdr (io/reader (io/file "/etc/xxx/default.conf"))] (接下来 (line-seq rdr)))
注意,从最初的序列开始,括号一层一层向外扩展的过程,就是变换的过程。
(with-open [rdr (io/reader (io/file "/etc/xxx/default.conf"))] (下一步 (map #(str/replace %1 "java" "clojure") (line-seq rdr)))))
现在我们得到的是经过替换之后的所有的行,但这些行必须要消费,我们把它输出到一个新的文件中,比如/etc/xxx/default.conf.clojuretmp
(with-open [rdr (io/reader (io/file "/etc/xxx/default.conf") wrt (io/writer (io/file "/etc/xxx/default.conf.clojuretmp"))] (doseq [line (map #(str/replace % "java" "clojure") (line-seq rdr))] (.write wrt (str line "\n"))))
这是什么呀,这么简单的问题弄得这么复杂?
其实文件处理会产生clojure所说的副作用,而且刚才的例子是lazy(懒)序列,可以处理任意大小的文件,不会造成内存不够。如果不考虑这个因素,那么看起来就更clojure了。
先定义一个fn,得到所有行的序列。
(defn read-all-lines [& paths] (with-open [rdr (io/reader (apply io/file paths))] (doall (line-seq rdr))))
总共有几行?
(count (read-all-lines "c:" "test" "test.txt"))
列出包含java的行?
(filter #(re-find #"java" %) (read-all-lines "c:" "test" "test.txt"))
包含java的有几行?
(count (filter #(re-find #"java" %) (read-all-lines "c:" "test" "test.txt")))
如果你不断的用clojure的方式去思考,慢慢的会从最开始的别扭变得非常自然流畅。
三、reader forms,这个真不好翻译
随着学习的深入,你肯定会回顾整个问题。用初看clojure写的程序代码看起毫无规律,真的如此吗?请参阅:http://clojure.org/reader
上面出现的任意一行代码都可以归到仅有的几个类别中。下面我翻译一下:http://clojure.org/evaluation ,翻译涉及到专业术语的规范,这里可能没有遵循。
我的词汇对应:
Evaluation 求值
expression 表达式
symbol 符号
form 不知道怎么翻译
意译开始:
clojure的代码由表达式组成,除了special form 和 macro,都是表达式。这话有点矛盾,不过special form 和 macro 经过处理之后就是表达式了,这样一来也不算矛盾,只是它们俩有点特殊而已。
求值过程是统一的,没有例外。表达式作为一个整体 ,求值,返回结果。
“abc" \a 1 nil true false :keywords 计算的结果都是自己。
注意,你在代码中加入这样一行也是可以的,虽然没有什么意义:abc" \a 1 nil true false :keywords
接下来就是符号。如果你的代码中有这样一行:
whoami
clojure如何处理呢?判断循序如下:
1、是special form吗?是special form的话,由read会特殊处理,这个special form 不多,没几个。 http://clojure.org/special_forms
2、是指向java类吗? (String. "abc"),这里的String就是,当然这种写法不过是一个macro,clojure会完成转换
3、在本地上下文中有绑定吗? (let [whoami "name"] whoami),那么前面一个whoami怎么回事?let是special form,所以clojure会处理。
4、当前名字空间中是否有绑定?
5、以上都不是,那就是出错了。
现在是list,规则如下
1、空的list就是自己
2、不空的list,是:special form,macro,function,三者之一。
按照这个规则,以下代码:
(count (filter #(re-find #"java" %) (read-all-lines "c:" "test" "test.txt")))
最外围的list,count是一个函数,带一个参数,内嵌的filter也是函数,带2个参数。#(re-find #"java" %) 是一个macro,re-find是一个函数,带2个参数,#"java"是macro,正则表达式,%由macro来解释,所以不用担心。read-all-lines是在当前名字空间中定义的,这个符号绑定到fn。后面的"c:"这样的表达式求值结果都是自己。
就先写到这里吧。有错误,请不用客气,直接指出。
标签:style http io os ar java for strong sp
原文地址:http://my.oschina.net/jianglibo/blog/335099