本文翻译自:Learn Clojure using records and protocols
当我对Clojure的括号不再疑惑后,另一个让我质疑为何要学习Clojure的问题是使用REPL很爽,但是我怎么来构建大型项目?".
实际上,由于我的面向对象编程经验,我其实要问的是"我怎么才能将函数封装到类似class的东西里面去?".
在本文中,我将会介绍一种类似于Java的方式来构建大型的Clojure项目.
通过这种方式,希望你在学习Clojure的时候不会有太大的差异感!
在Java中,我们出于各种目的而使用类.例如典型的使用Spring的web应用,你会看到类似下面的结构:
- Data transfer objects(DTO)
- Services(REST API,controllers,DAO)
- Rich object
DTO实际上就是个结构体,他没有任何的行为(即方法).为了最小化样板代码,我趋向于使用pulibc final属性去实现DTO.我认为DTO就是个模板(schema),它就像一个REST服务输出的文件.但是我发现很多客户端开发人员可不关注这个,而只关注代码形式.有时你会看到DTO被作为数据库访问的一部分.这些DTO被称为贫血模型.(译者注:猜测作者的意思是,有些开发人员不管类是DTO还是DAO,只要结构相同就随便用!)
Service实际上是包含了方法和注入了辅助Service的单例对象.不同的辅助Service提供了不同的服务.除了实现了接口外,Service只是包含了方法,和占位符很类似.单例模式导致的一个问题就是,新手无法预见到多线程里共享相同的实例出现意外的结果.他们将状态保存到私有属性里,而不需要从一个对象传递到另一个对象.很方便,但是是错误的做法.
Rich object是面向对象语言中的思想.即将数据及和数据相关的操作封装到一个类里面.我可没说getter和setter是相关操作!但是,rich object类在项目中用得较少.取而代之的是使用DTO作为Service的输入和输出.使用DAO来访问数据库,然后返回DTO.我没说这种方法是错误的,我好奇的是,既然对于目前的Java架构是好是坏我们都无法确认,那为什么还要强求使用Clojure去实现类似的东西呢?
本文翻译自Functions in Clojure
本文包括如下内容:
- 如何定义函数
- 如何执行函数
- 多元数函数(Multi-arity Functions)
- 不定参函数(Variadic Functions)
- 高阶函数
- 其它函数相关内容
版权:
This work is licensed under a Creative Commons Attribution 3.0 Unported License (including images & stylesheets). The source is available on Github.
针对Clojure版本
Clojure 1.5
简介
Clojure是函数式编程语言.自然的,函数是Clojure非常重要的一部分.
如何定义函数
函数定义一般使用defn宏:
(defn round
[d precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
类型提示有时能避免编译器使用反射,从而能生成更高效的字节码.但是,基本上你没必要使用类型提示.后期优化时再考虑.
函数可以添加注释文档,给API添加文档说明是个好习惯:
(defn round
"Round down a double to the given precision (number of significant digits)"
[d precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
在Clojure中函数参数可以有类型提示,不过是可选的.
(defn round
[^double d ^long precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
函数还可以定义前置和后置条件来限制函数的参数和返回值.
(defn round
"Round down a double to the given precision (number of significant digits)"
[^double d ^long precision]
{:pre [(not-nil? d) (not-nil? precision)]}
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
在上面的例子中,我们使用了前置条件来检查两个参数是否为nil.
not-nil?宏(或函数),没有在该例子中展示,我们假设它已经在其它地方实现了.
本文翻译自Mathematics with Clojure
本文包含了使用Clojure的内建函数,扩展包和部分JDK功能进行数学计算.
版权:
This work is licensed under a Creative Commons Attribution 3.0 Unported
License (including images & stylesheets). The source is available on
Github.
准备
这里的一些例子使用了math.numeric-tower和math.combinatorics库.所以需要
你在代码的命名空间中包含如下代码:
(:require [clojure.math.numeric-tower :as math]
[clojure.math.combinatorics :as combo])
或者在REPL里载入:
(require '[clojure.math.numeric-tower :as math])
(require '[clojure.math.combinatorics :as combo])
方法
简单计算
(+ 3 4) ;=> 7
(- 3 4) ;=> -1
(* 3 4) ;=> 12
(/ 3 4) ;=> 3/4 (an exact ratio)
(/ 3.0 4) ;=> 0.75
(inc 5) ;=> 6
(dec 5) ;=> 4
想要计算整数的除法,取余和取模,请看quot,rem,mod
想计算指数的平方根,四舍五入,上下限,绝对值和最大公约数/最小公倍数,请看math.numeric-tower
三角函数
使用Java平台提供的函数:
Math/PI ;=> 3.14159...
(Math/sin x)
(Math/cos x)
(Math/tan x)
还有很多的函数,你可以查看java.lang.Math
组合数学
对于组合数学相关函数(比如:combinations和permutations),请看math.combinatorics
本文翻译自Working with Files and Directories in Clojure
本文介绍使用Clojure的clojure.java.io命名空间下的函数以及JDK相关方法来操作文件和目录.
版权:
This work is licensed under a Creative Commons Attribution 3.0 Unported License (including images & stylesheets). The source is available on Github.
准备
请注意,在下面的例子中,"io"是clojure.java.io命名空间的简写引用.请确保你的ns宏包含如下代码:
(:require [clojure.java.io :as io])
或者在REPL中加载了:
(require '[clojure.java.io :as io])
方法
将文件读到字符串中
(def a-long-string (slurp "foo.txt"))
注意,你可以给slurp函数传递url.请看slurp文档
一行一行的读取文件
假设你想针对文件的每行内容调用my-func函数,并返回结果序列:
(with-open [rdr (io/reader "foo.txt")]
(doall (map my-func (line-seq rdr))))
doall函数是必须的,因为map函数返回的是个lazy序列.line-seq返回的每行内容没有是去除了末尾的回车标记的,且空行将返回空字符串.
本文翻译自Strings
本文内容包括使用Clojure内建的函数和标准库来操作字符串,以及JDK中的相关操作.
版权:
This work is licensed under a Creative Commons Attribution 3.0 Unported License (including images & stylesheets).
源代码在Github上.
概述
- Clojure中的字符串就是Java字符串.你可以使用Java中的相应方法来对其进行处理.
- Java字符串是不可变的,所以能很方便的在Clojure中使用
- 你不能给Java字符串添加元数据
- Clojure提供一些方便的符号
"foo" java.lang.String
#"\d" java.util.regex.Pattern (正则表达式,匹配一个数字)
\f java.lang.Character (表示'f')
- 注意:
人类的大脑和计算机之间还是有很大的差异的.所以Java的字符串(UTF-16编码)并不能像人们所认为的那样很好的和字符一一对应.比如说,一个Unicode的位和我们所认为的字符的一位是无法对应的(像韩国韩语尊宝,这些词由多个字符组成.)同样的,一个Unicode位可能是由2个UTF-16字符组成.
预备知识
一些例子用到了clojure.string,clojure.edn和clojure.pprint.我们假设你的ns宏里面已经包含了如下内容:
(:require [clojure.string :as str]
[clojure.edn :as edn]
[clojure.pprint :as pp])
或者在REPL里面,你需要载入:
(require '[clojure.string :as str])
(require '[clojure.edn :as edn])
(require '[clojure.pprint :as pp])
简介
学习一门语言最好的方法就是去使用它。我们就从一个小例子来学习Clojure语法.
核心语法
首先先来看一下Clojure的核心语法。
Clojure的使用的是Lisp语法,又叫S表达式。核心语法非常的简单。但是对于熟悉c系语法(c,c++,java)的朋友来说,第一次接触会非常的不习惯。
- 所有代码以"("开始,")"结束。即所有的代码都被()包裹
- "("后的第一个数据被当作函数或者宏来调用
- 接着的数据被当作参数传递
- 如果想原样返回数据,在"("前面添加"'"。此写法是(quote (...))的简写
以前面的hello world程序为例。
它以"("开始,后面跟的是函数println,接着空格跟的是参数"HelloWorld",最后")"结尾。
了解了如上规则,你就基本学会了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 + 1^a |
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.is_a? 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是JVM上的一种语言,其语法是Lisp格式,所以称为是JVM上的Lisp方言。
Clojure依赖于JDK。请确保你的机器上安装了JDK6+。Clojure的安装非常的简单。
可以分为两种方式:
通过Leiningen是比较普遍也很方便的方式。我们先通过Leiningen来安装。
通过Leiningen安装
Leiningen和Maven比较类似,是Clojure语言的管理工具。其实它就是基于Maven
构建的。我们先来安装Leiningen
- 首先下载lein
script
- 将lein script放置到你的path下(linux下直接放到用户目录下即可),保存文
件名叫lein。
- 将其权限设置为可执行(chmod a+x lein)
- 打开shell,输入lein即可
leiningen会自动下载需要的依赖。运行完成后,Clojure开发环境即安装完成。
手动安装
leiningen需要以项目为单位才能运行。比如你想运行一个hello.clj程序,如果
你通过leiningen来运行,则hello.clj需要在lein项目内,且设置为core,才能
运行,比较麻烦(这个问题在Maven中同样存在)。
而通过手动安装可以解决这个问题。
- 首先下载Clojure,
目前稳定版本是1.5.1
- 解压到任意位置,其中最主要的文件就是clojure-1.5.1.jar
- 在path下编写脚本clj,输入如下内容
java -jar /home/ivan/soft/clojure-1.5.1/clojure-1.5.1.jar $1
#clojure.jar请输入你本机的路径
这样就可以执行单个的Clojure文件了