Clojure教程-基本语法-01

目录

  • 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) !k not k not k or !k
(inc a) a++、++a、a += 1、a + 1a a += 1、a + 1 a += 1
(/ (+ x y) 2) (x + y) / 2 (x + y) / 2 (x + y) / 2
(instance? java. util.List al) al instanceof java.util.List isinstance(al,list) al.isa? Array
(if (not a) (inc b) (dec b)) !a ? b + 1 : b – 1 b + 1 if not a else b-1 !a ? b + 1 : b – 1
(Math/pow 2 10)c Math.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所包含的字面量。

Type Example(s)
Boolean true,false
Character \a
Keyword :tag,:doc
List (1 2 3),(println “foo”)
Map {:name “Bill”,:age 42}
Nil nil
Number 1,4.2
Set #{:snap :crackle :pop}
String “hello”
Symbol user/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。这里没有将其翻
译为命名空间,主要是怕有误解。

你可能感兴趣的:(Clojure)