几分钟内学习 Clojure

几分钟内学习 Clojure - 技术翻译 - 开源中国社区
https://www.oschina.net/translate/learn-clojure-in-minutes

此处获取源码: test.clj
Clojure是JVM上的一个LISP语言变种。它比Common Lisp更强调纯函数式编程,但提供了一些STM工具以处理它所引入的状态问题。
这个组合使并行处理更加简单,并且通常是自动的。
(你需要Clojure 1.2或以上版本。)

; 分号作为注释的开始

; Clojure 用一种把元素用括号括起来的像列表一样的方式来书写,元素之间用空格隔开

; clojure 解释器会把第一个元素当做是函数或者宏调用,其他的都作为参数

; 下面这个函数用于设置当前的命名空间

(ns
test
)

; 更多基本的例子:

; str 函数会用它所有的参数创造一个字符串

(str
"Hello"
" "
"World"
) ; =>
"Hello World"

; 数学运算很直观,不过是前缀表达式

(+ 1 1) ; => 2

(- 2 1) ; => 1

(* 1 2) ; => 2

(/ 2 1) ; => 2

; 相等比较使用 “=”符号

(= 1 1) ; =>
true

(= 2 1) ; =>
false

; 你也不必担心逻辑运算

(not
true
) ; =>
false

; 嵌套方式正如你预料的那样

(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2

; 类型系统

;;;;;;;;;;;;;

; Clojure 使用java对象类型来表示 布尔值、字符串和数字

; 使用 class函数来检测它们.

(class 1) ; 整形字面值默认是java中的Long类型

(class 1.); 浮点字面值对应着java中的Double类型

(class
""
); 字符串总是用双引号括起来,并且对应着java中的Sring类型

(class
false
) ;布尔值对应着java中的Boolean类型

(class nil); null值被称为 nil(英语含义:无、零点)

; 如果你想创建一列数据字面值, 使用一个单引号 ' 来防表达式被解析执行

'(+ 1 2) ; => (+ 1 2) ;这里没有返回3

; (上面表达式和(quote (+ 1 2)) 等价,不过更简洁

; 你可以运算一个引用列表

(
eval
'(+ 1 2)) ; => 3

; 集合和序列

;;;;;;;;;;;;;;;;;;;

; 向量和列表也是java类哦!!

(class [1 2 3]); => clojure.lang.PersistentVector

(class '(1 2 3)); => clojure.lang.PersistentList

;书写一个列表形如(1 2 3)一样简单, 但是我们不得不把它“引”(前面加个单引号)起来

;这样就能防止解释器把它当做一个函数来解析

;另外,(list 1 2 3) 和 '(1 2 3) 等价

;列表和向量都是集合:

(coll? '(1 2 3)) ; =>
true

(coll? [1 2 3]) ; =>
true

; 只有列表是序列.(序列是有顺序的)

(
seq
? '(1 2 3)) ; =>
true

(
seq
? [1 2 3]) ; =>
false

; 序列是列表一种逻辑上的接口,可以懒加载.

;
"懒"
意味着可以定义无穷序列,就像下面一样:

(range 4) ; => (0 1 2 3)

(range) ; => (0 1 2 3 4 ...) (一个无穷序列)

(take 4 (range)) ; (0 1 2 3)

; 使用cons 来追加一个元素到列表或者向量的头部

(cons 4 [1 2 3]) ; => (4 1 2 3)

(cons 4 '(1 2 3)) ; => (4 1 2 3)

; 使用conj追加一个元素到列表的头部,或者向量的尾部,

(conj [1 2 3] 4) ; => [1 2 3 4]

(conj '(1 2 3) 4) ; => (4 1 2 3)

; 使用concat来连接列表和向量

(concat [1 2] '(3 4)) ; => (1 2 3 4)

; 使用filter, map 来进行列表计算

(map inc [1 2 3]) ; => (2 3 4)

(filter even? [1 2 3]) ; => (2)

; 使用reduce 来进行化繁为简 (map
/reduce
思想就来自于lisp)

(reduce + [1 2 3 4])

; = (+ (+ (+ 1 2) 3) 4)

; => 10

; Reduce 可以使用一个初始值

(reduce conj [] '(3 2 1))

; = (conj (conj (conj [] 3) 2) 1)

; => [3 2 1]

; 函数

;;;;;;;;;;;;;;;;;;;;;

; 使用fn来创建一个函数。所有的函数都有返回值,就是它的最后一个表达式

(fn []
"Hello World"
) ; => fn

; (你需要额外的括号去调用它)

((fn []
"Hello World"
)) ; =>
"Hello World"

;你可以使用def来创建变量

(def x 1)

x ; => 1

; 将函数赋值给一个变量

(def hello-world (fn []
"Hello World"
))

(hello-world) ; =>
"Hello World"

; 你可以使用defn来简化定义过程

(defn hello-world []
"Hello World"
)

;[] 是函数的参数列表

(defn hello [name]

(str
"Hello "
name))

(hello
"Steve"
) ; =>
"Hello Steve"

; 你也可以使用下面这种简写方式

(def hello2

(str "Hello " %1))

(hello2
"Fanny"
) ; =>
"Hello Fanny"

; 你可以创建拥有可变参数的函数

(defn hello3

([]
"Hello World"
)

([name] (str
"Hello "
name)))

(hello3
"Jake"
) ; =>
"Hello Jake"

(hello3) ; =>
"Hello World"

; 函数允许将参数打包成列表 (有点类似python中的*)

(defn count-args [& args]

(str
"You passed "
(count args)
" args: "
args))

(count-args 1 2 3) ; =>
"You passed 3 args: (1 2 3)"

; 你可以将普通参数和列表参数混合使用

(defn hello-count [name & args]

(str
"Hello "
name
", you passed "
(count args)
" extra args"
))

(hello-count
"Finn"
1 2 3)

; =>
"Hello Finn, you passed 3 extra args"

; 哈希表

;;;;;;;;;;

(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap

; 关键字类似字符串,但是做了一些性能上的优化

(class :a) ; => clojure.lang.Keyword

; Maps 的键可以是任意类型,但是通常推荐使用keywords

(def stringmap (
hash
-map
"a"
1,
"b"
2,
"c"
3))

stringmap ; => {
"a"
1,
"b"
2,
"c"
3}

(def keymap (
hash
-map :a 1 :b 2 :c 3))

keymap ; => {:a 1, :c 3, :b 2} (不保证顺序)

; 顺便说一下, 逗号只是为了看着更清晰,其他都和空格一样,什么都不做.

; 从一个map中检索一个值,可以直接把这个map当做函数调用(这个NB)

(stringmap
"a"
) ; => 1

(keymap :a) ; => 1

; 关键字也可以当做函数来调用,从一个map中检索值(这个更NB)

(:b keymap) ; => 2

; stings 可没有这个功能,所以下面会抛出异常。(这也是为什么推荐使用keywords)

;(
"a"
stringmap)

; => Exception: java.lang.String cannot be cast to clojure.lang.IFn

; 检索一个不存在的值会返回nil

(stringmap
"d"
) ; => nil

; 使用assoc 向一个map中添加新的键值对。

(assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}

; 请记住, clojure 类型是不可变的!

keymap ; => {:a 1, :b 2, :c 3}

; 使用dissoc 来删除key(可以删除多个)

(dissoc keymap :a :b) ; => {:c 3}

; 集合

;;;;;;

(class

{1 2 3}) ; => clojure.lang.PersistentHashSet

(
set
[1 2 3 1 2 3 3 2 1 3 2 1]) ; =>

{1 2 3}

; 使用con来添加新值

(conj

{1 2 3} 4) ; => #{1 2 3 4}

; 使用disj删除原有值

(disj

{1 2 3} 1) ; => #{2 3}

; 直接将
set
当做函数来测试是否包含某个值(NB)

(

{1 2 3} 1) ; => 1 (有就返回原有的值)

(

{1 2 3} 4) ; => nil (没有就返回nil)

; clojure.sets 命名空间包含更多的函数

; 一些有用的形式

;;;;;;;;;;;;;;;;;

; clojure中的逻辑结构都是宏, 看起来也没什么不同

(
if
false
"a"
"b"
) ; =>
"b"

(
if
false
"a"
) ; => nil

; 使用
let
来创建临时绑定

(
let
[a 1 b 2]

(> a b)) ; =>
false

; 执行多条语句,返回最后一条语句

(
do

(print
"Hello"
)

"World"
) ; =>
"World"
(prints
"Hello"
)

; 所有的函数都包含一个隐式的
do

(defn print-and-say-hello [name]

(print
"Saying hello to "
name)

(str
"Hello "
name))

(print-and-say-hello
"Jeff"
) ;=>
"Hello Jeff"
(prints
"Saying hello to Jeff"
)

;
let
绑定也是哦

(
let
[name
"Urkel"
]

(print
"Saying hello to "
name)

(str
"Hello "
name)) ; =>
"Hello Urkel"
(prints
"Saying hello to Urkel"
)

; 模块

;;;;;;;;;;;;;;;

; 使用“use”来获得一个模块中所有的函数

(use 'clojure.
set
)

; 现在我们可以使用集合操作

(intersection

{1 2 3} #{2 3 4}) ; => #{2 3} 求交集

(difference

{1 2 3} #{2 3 4}) ; => #{1} 求差集

; 你可以只导入一个函数子集(例如下面只包含交集函数)

(use '[clojure.
set
:only [intersection]])

; 使用reqire来导入一个模块

(require 'clojure.string)

; 使用/从一个模块中调用函数

(clojure.string
/blank
?
""
) ; =>
true

; 你可以在导入模块的时候自定义名称

(require '[clojure.string :as str])

(str
/replace
"This is a test."

"[a-o]" str/upper-case) ; => "THIs Is A tEst."

; (

"" denotes a regular expression literal)

; 你可以使用
":require"
从一个命名空间中引入模块(use也可以,但是别这么做)

; 如果你使用:require的话,就没必要把模块“引”(前面加个单引号)起来了.

(ns
test

(:require

[clojure.string :as str]

[clojure.
set
:as
set
]))

; Java

;;;;;;;;;;;;;;;;;

; java 拥有一个庞大的各种用途的标准库,你一定迫不及待想学习如何在clojure中使用这些库

; 使用
import
类引入java模块(这个还好没变化)

(
import
java.util.Date)

; 你也可以从一个命名空间中引入

(ns
test

(:
import
java.util.Date

java.util.Calendar))

; 类名字后加个”."用来创建一个对象

(Date.) ; <a
date
object>

; 使用. 来调用方法. 或者使用“.方法名"简写的方式

(. (Date.) getTime) ; <a timestamp>

(.getTime (Date.)) ; 和上面一样哦

; 使用/ 来调用静态方法

(System
/currentTimeMillis
) ; <a timestamp> (system is always present)

; 使用 doto 来处理可变的类<span style=
"font-family:宋体;"

,所有的函数始终用最初的那个对象值,最后还是返回最初的那个对象<
/span
(
import
java.util.Calendar)

(doto (Calendar
/getInstance
)

(.
set
2000 1 1 0 0 0)

.getTime) ; => A Date.
set
to 2000-01-01 00:00:00

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容