Clojure笔记
Clojure Forms
Boolean
- typical value: true, false
- useful function:
- not
- and
- or
Nil
- typical value: nil 只有false与nil会被计算为false,其它的都为true
Character
- typical value: .̧̱..
Number
- typical value: 1 2
- useful function:
- +, -, *
- /(分数形式),quot(商),rem(余数)
- inc,dec
- min, max
- =
,<,<
,>,>= - zero?,pos?,neg?,number?
Symbol
- typical value: user/foo
String
- typical value: “hello”
- useful function:
- str: 拼接多个字符串
- subs: 子字符串(0为开始下标)
- string?
- print & println: 后者自动添加一个新行
regex regex
- re-pattern:创建一个正则表达式,
(re-pattern "[abcd]")
等价于 #“[abcd]“,
KeyWord
- typical value: :tag :doc
List
- typical value: (+ 1 3) (println “foo”)
Vector
- typical value: [1 2 3]
Map
- typical value: {:key1 val1, :key2 val2 …} {“Lisp” “McCarthy”
“Clojure” “Hickey”}
Set
- typical value: #{:val1 :val2 …}
seq operation
clojure的sequence都是不可变的.
- first: like car in scheme
- rest: like cdr in scheme
- cons: like cons in scheme (Construct)
- conj: 将单个元素插入seq,对于list是插入到最前面,对于vector是插入最后面
- into: 将后一个seq插入到前一个seq,对list是插入最前面,对vector是插入最后面
seq library
下面的函数对任何类型的sequence都有效, 而且这些操作除极少数外基本都返回 lazy sequence.
empty? : same as null? in scheme.
seq : 如果coll为空则返回 nil, 否则返回一个seq.
range: (range start? end step?)
1
2(range 5)
;;=> (0 1 2 3 4)repeat:
1
2
3(repeat 5 "a") => ("a" "a" "a" "a" "a" )
(repeat "a") => return an infinite seq
(take 5 (repeat "a")) => ("a" "a" "a" "a" "a" )(iterate f x): f可以看做是一个后继函数,返回一个无限序列
1
(take 5 (iterate inc 1)) => (1 2 3 4 5)
take: 从一个无限序列中抽取指定个数的元素组成序列
cycle:返回一个无限的序列
1
(take 10 (cycle (range 3))) => (0 1 2 0 1 2 0 1 2 0)
interleave:
1
(interleave '(1 2 3) '(a b c) '(x y z)) => (1 a x 2 b y 3 c z)
interpose:
1
(interpose \, ["aa" "bb" "cc"]) => ["aa" \, "bb" \, "cc" \,]
(join sep seq) : 返回将seq中的元素用sep分割后的字符串
list:生成一个list
vector: 生成一个vector
hash-set:
hash-map:
filter:
map:
reduce:
split-at:将一个seq根据index分割为2个seq
split-with: 将一个seq根据一个predictive function的返回值分割为2个seq,true
为一个seq,false为一个seqevery?
some
not-every?
not-any?
基本语法
function definition
匿名函数(fn)
(fn [x y] (+ x y))
创建一个匿名函数,fn
和lambda
类似,fn还有一个简
写形式#(+ %1 %2)
.如果只有一个参数,那么可以用 % 代替 %1def: 可以将一个匿名函数与一个name关联起来,和
scheme
中的define
类似1
(def my-add (fn [x y] (+ x y)))
defn: 是def 与 fn 的简写
1
(defn my-add [x y] (+ x y))
参数的解构
1
2
3
4(defn my-add [x y [a b]]
(+ x y a b))
(my-add 1 2 [3 4])
; => 10Arities(可以看做是函数多态) 根据参数的不同而执行不同的动作
1
2
3
4
5(defn square-or-multiply
"squares a single argument, multiplies two arguments"
([] 0)
([x] (* x x))
([x y] (* x y)))这应该也是为什么clojure中doc string是放在函数名之后而不是参数名之后的原因
递归: 由于jvm的关系,clojure不会自动进行尾递归优化,在尾递归的地方,你应该明 确的使用
recur
这个关键词,而不是函数名1
2
3
4
5
6
7
8
9(defn my-add [x y]
(if (zero? x)
y
(my-add (dec x) (inc y))))
(defn my-add [x y]
(if (zero? x)
y
(recur (dec x) (inc y))))第一个不会进行尾递归优化,第二个会进行尾递归优化
loop:和
recur
配合可以实现和循环类似的效果1
2
3
4(loop [i 10 j 10]
(if (zero? i)
j
(recur (dec i) (inc j))))和scheme中使用 let 创建一个函数很相似
1
2
3
4(let loop ([i 10] [j 10])
(if (zero? i)
j
(loop (- i 1) (+ j 1))))curry 只提供部分参数给函数,比如我们可以这样定义
add3
1
2
3(def add3 (partial + 3))
(add3 4)
;=> 7partial 接受有一个函数以及部分参数,返回一个函数
comp: 可以生成一个函数,f(g(x))等价于
((comp f g) x)
基本控制结构
if
cond
let
letfn
模块与namespace
require: 导入clojure模块
use: 导入clojure模块,与require的区别是,use会将指定模块的名字导入当前的
namespace,所以在引用时就不需要添加模块名作为前缀,而require则需要,一般情况
下推荐useimport: 导入java的类
1
2
3
4(import 'java.util.Date)
; (new Date)
(import '(java.util.regex Pattern Matcher))
; only import Pattern and Matcher in java.util.regexns:创建一个命名空间,ns是一个宏,所以后面的参数不需要quote,而且注意后面的
require,use,import是以keyword的形式给出1
2
3
4
5(ns com.example.library
(:require [clojure.contrib.sql :as sql])
(:use (com.example one two))
(:import (java.util Date Calendar)
(java.io File FileInputStream)))
State
ref
ref
deref(@)
ref-set: 需要使用 dosync来避免竞争条件
dosync: 被dosync包裹的表达式要么全部执行成功,要么都不执行,并且保证每一步
都不出现竞争条件,和数据库的存储过程很类似,实现了ACI,数据库的存储过程一般
实现了ACID.1
2
3
4
5
6
7(def current-track (ref "Venus, the Bringer of Peace"))
;; -> #'user/current-track
(def current-composer (ref "Holst"))
;; -> #'user/current-composer
(dosync
(ref-set current-track "Credo")
(ref-set current-composer "Byrd"))alter :
(alter ref update-fn & args...)
,@ref
作为update-fn的第一个参
数(这也是为什么下面的代码要用conj代替cons的原因),args作为剩余的参数,alter
可以看作是dosync,ref-set,deref的简写1
2
3
4
5
6(def message (ref ()))
(defn navie-add-message [msg]
(dosync (ref-set message (cons msg (deref message)))))
;;; identical
(defn add-message [msg]
(alter message conj msg))
atom
atom和ref很类似,但是atom当你仅仅只是需要原子的更新单个值时,使用更简便.
- atom:
(def (atom ()))
- deref(@)
- reset!: 更新原子的值
- swap! :
(swap! r update-fn & args)
用一个函数调用来生成新的值
Macro
几个简写
反引号(`) : 和scheme相同
~, ~@ 和scheme的 , ,@ 的含义相同,之所以用 ~ 是因为clojure中 , 与空格等价
id# :在一个标识符背后加上 # 意味着生成一个唯一的symbol, 比如 foo# 实际可 能就是 foo_004
可以看作是let与gensym的等价物,这在避免符号捕捉时很有用.1
2
3
4
5
6
7
8(defmacro and
([] true)
([x] x)
([x & rest]
`(let [and# ~x]
(if and#
(and ~@rest)
and#))))注意上面的 and# 不需要使用 ~ 来求值,因为 and# 本身就是一个独一无二的符号
调用java
- new : 创建一个对象.
(new String)
- . :
(. target name & args)
- set! :
(set! (. target name) value)
上面的3个操作符已经足够,但是为了方便,还有以下几个操作符
ClassName/field: 只对静态变量或者方法有效
1
2
3
4Integer/MIN_VALUE
; => -2147483648
(Integer/parseInt "101")
; => 101.method :
(.method object args)
可以看做等价于(. object method args)
,
但是要记住 .method并不是真正的第一类函数对象,所以你不能直接将它传递给 map,filter这类函数1
2(map #(.toUpperCase %) ["one" "two" "three"])
; => ("ONE" "TWO" "THREE")
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!