您的位置:首页 > 其它

Clojure教程-基本语法-01

2013-12-21 11:25 337 查看

目录

1 简介

2 核心语法

3 代码功能

3.1 读取简繁字典

简介

学习一门语言最好的方法就是去使用它。我们就从一个小例子来学习 Clojure语法.

核心语法

首先先来看一下Clojure的核心语法。

Clojure的使用的是Lisp语法,又叫S表达式。核心语法非常的简单。但是对于熟悉c系语法(c,c++,java)的朋友来说,第一次接触会非常的不习惯。

所有代码以”(“开始,”)”结束。即所有的代码都被()包裹

“(“后的第一个数据被当作函数或者宏来调用

接着的数据被当作参数传递

如果想原样返回数据,在”(“前面添加”‘”。此写法是(quote (…))的简写

以前面的hello world程序为例。
(println "Hello World")


它以”(“开始,后面跟的是函数println,接着空格跟的是参数”Hello World”,最后”)”结尾。了解了如上规则,你就基本学会了Clojure的大部分语法。下面比较一下Clojure,Java,Python,Ruby的一些语法.
Clojure 表达式对应的 Java 语法对应的 Python 语法对应的 Ruby 语法
(not k)!knot knot k or !k
(inc a)a++、++a、a += 1、a + 1aa += 1、a + 1a += 1
(/ (+ x y) 2)(x + y) / 2(x + y) / 2(x + y) / 2
(instance? java. util.List al)al instanceof java.util.Listisinstance(al,list)al.isa? Array
(if (not a) (inc b) (dec b))!a ? b + 1 : b – 1b + 1 if not a else b-1!a ? b + 1 : b – 1
(Math/pow 2 10)cMath.pow(2, 10)pow(2, 10)2 ** 10
(.someMethod someObj “foo” (.otherMethod otherObj 0))someObj.someMethod(“foo” , otherObj.otherMethod(0))someObj.someMethod(“foo” , otherObj.otherMethod(0))someObj.someMethod(“foo” , otherObj.otherMethod(0))
可以看出Clojure的语法有高度的一致性,即使你不熟悉S表达式,但是依据上面的原则,可以看懂它想表达的是一个什么意思。而对于其他三门语言,如果你没有一个个的学习相应的语法,你还是比较难理解它的意思的。

代码功能

了解了核心语法,我们就可以来编写代码了。我们要编写的代码功能很简单,进行简繁翻译,其中过滤不需要翻译的文字。我们将分几步来完成:

读取简繁字典

进行简繁翻译

读取不需要翻译的文字

过滤不翻译的文字

多线程执行

提供给Java调用

打包

读取简繁字典

语法点

查看Clojure API

第一个函数的编写

Namespace的引入

正则表达式

Clojure字面量

我们先看第一个功能。我们要读取简繁字典。简繁字典其实就是简繁对照的文件,我这里叫jfmap.clj

格式如下:
万 萬 与 與 丑 醜 专 專 业 業 丛 叢 东 東 丝 絲 丢 丟 两 兩 严 嚴 丧 喪 个 個 丬 爿


这里只是简单的列了一点。具体内容请见附件。有了这个文件,我们如何把内容读出来呢?熟悉Java的都知道,我们要创建文件流来读取,然后要打开流,循环读取,最后关闭流,还要抓异常。很繁琐。在Clojure中如何处理呢?Clojure提供了slurp函数,可以根据提供的路径将文件内容读入。API如下:
clojure.core/slurp
([f & opts])
Opens a reader on f and reads all its contents, returning a string.
See clojure.java.io/reader for a complete list of supported arguments.


OK。我们知道了要用什么函数。那么根据API和上面说的总规则,我们来写代码.

首先是”(”

然后是函数名slurp

空格,参数。这里是要读取的文件路径

最后”)”

(slurp "jfmap-path")


你可以在REPL里面去实验这行代码的执行结果。执行此行代码,clojure会将jfmap.clj的内容以字符串的形式全部读入。接着呢?要做简繁翻译,字符串肯定不方便我们的操作。很明显map才是最适合的数据结构。那么我们如何将字符串变成map呢?

我们只能求助于API了,你可以在Clojure的Index页面搜索map,可以找到hash-map函数。它的API说明如下:
hash-map
function
Usage: (hash-map)
(hash-map & keyvals)
keyval => key val
Returns a new hash map with supplied mappings.  If any keys are
equal, they are handled as if by repeated uses of assoc.


根据提供的映射关系返回一个新的hashmap。而这里我们是一个字符串,如何提供映射关系呢?按照空格将文字切开就行了嘛!!继续找API。Java里有split方法,Clojure里有没有相应的函数呢?试试再说。。有了!在clojure.string的Namespace1中,我们找到了叫split的函数!
split
function
Usage: (split s re)
(split s re limit)
Splits string on a regular expression.  Optional argument limit is
the maximum number of splits. Not lazy. Returns vector of the splits.


通过正则表达式来切割字符串。看着挺像,先用再说!我们有repl嘛!直接在repl里面输入
(split "万 萬" #" ")


执行!Oops,报错了!
CompilerException java.lang.RuntimeException: Unable to resolve symbol: split in this context


找不到split?!如果在Java中报类似的错误,你会想到什么?没有引入包阿!这里也是。在Java中会默认引入java.lang包,同理在Clojure中会引入clojure.core包。其他包则要自己引入,这里split在clojure.string包中。所以你需要引入clojure.string包。
(require 'clojure.string)


这也就是调用了require函数来进行引入!为什么clojure.string前面有个单引号呢?想想核心语法!这里暂不展开说!给大家留个思考题!!后续会专门对NameSpace引入做详细介绍!光引入还没用!调用代码也需要修改!
(clojure.string/split "万 萬" #" ")


你可能要吐槽了!既然引入了,为什么还要加包名前缀?!我们可以和Java作个比较!如果这里是Java的话,那么我们在调用split的时候,实际上是需要一个类作为前缀的,比如StringUtils.split()!但是在clojure中并没有类的概念!包(Clojure叫NameSpace)下面只有函数,所以它使用NameSpace来确保函数的唯一性引用!

当然了每次都要写这么长的NameSpace的名字也是挺烦人的。Clojure提供了简写.
(require ['clojure.stirng :as 'cstr])
(cstr/split "万 萬" #" ")


:as是Keyword,是Clojure字面量的一种。它和String很类似,不过有些区别,它比String有更多的功能。下表是Clojure所包含的字面量。
TypeExample(s)
Booleantrue,false
Character\a
Keyword:tag,:doc
List(1 2 3),(println “foo”)
Map{:name “Bill”,:age 42}
Nilnil
Number1,4.2
Set#{:snap :crackle :pop}
String“hello”
Symboluser/foo,java.lang.String
Vector[1 2 3]
ok.终于得到了我们要的结果。这里的#” “是正则表达式(这是你遇到的第一个特殊语法,学习方法—死记!!),它构建了Java中的Pattern,所以正则表达式内容和Java完全相同,这里就不废话了。你只需要记住其语法就行了。

切开了字符串,我们来生成map吧!如何生成呢?你应该有答案了吧?
(hash-map (cstr/split "万 萬" #" "))


又报错了!
IllegalArgumentException No value supplied for key: ["万" "萬"]  clojure.lang.PersistentHashMap.create (PersistentHashMap.java:77)


不合法的参数!!split得到的是个Vector([]包裹的数据结构是Vector),而hash-map要的参数类似于Java中的可变参数!如何匹配这两者呢?Clojure中提供了apply函数!API如下
clojure.core/apply
([f args] [f x args] [f x y args] [f x y z args] [f a b c d & args])
Applies fn f to the argument list formed by prepending intervening arguments to args.


此函数有点特别!它的第一个参数是函数,后面是该函数所需要的参数!知道怎么调用吗?
(apply hash-map (cstr/split "万 萬" #" "))


终于成功了!!我们看到了结果
{"万" "萬"}   ;以{}包裹的数据结构是map


最后呢!我们需要对jfmap.clj的内容进行处理!So easy!
(apply hash-map (cstr/split (slurp "jfmap-path") #" "))


脚注:

1 Namespace和Java中的包类似,但是在Clojure中叫Namespace。这里没有将其翻

译为命名空间,主要是怕有误解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: