首先推荐两个目前正在学的免费学习资源:
Functional programming in Clojure
Clojure for the Brave and True
都是英文的,第一个是边学边练的形式,很容易测试自己是否真的学懂了,在实践中也能学到更多的东西;第二个是一本Clojure的入门书籍,第二章介绍了Emacs的安装配置以及一些常用的快捷键,很适合作为新手入门教程。
一、注释和命名空间
目前学到的就两种:
; 为单行注释
#_ 可以注释掉之后的一个完整形,即#_后面第一个括号里面的内容
(ns namespace) ;改变命名空间后可以直接用该空间定义的函数变量等
命名空间用-,而对应的实际文件名用下划线_
user=> (use example.hello) ;使用use时,在后面的namespace前需要加单引号,否则会报错
java.lang.ClassNotFoundException: example.hello (NO_SOURCE_FILE:1)
(use 'clojure.repl)
(doc +) ;可以查看函数的说明
(user/clojuredocs function)
;to see some examples for function. This should work for most of the built-in functions.
二、数据类型
数值Numbers:11 2/3 3.2 整数 分数 浮点数等
运算符 + - * / mod ;mod返回两数相除的余数
逻辑 not = == >= <= > <
(== 42 42) ;=> true
(== 5.0 5) ;=> true
(= 5.0 5) ;=> false ! 即==要求值相等就行,=要求类型也一致,条件更严格
字符串Strings:"hello" "abc"用双引号表示
字符Characters:\x \b \? \√ 用反斜杠表示特定字符
关键字Keywords::a :b :keyword 用英文的冒号开头表示
布尔值Booleans: true false nil 均为小写,nil表示为空,逻辑判断上是属于false的,但是跟false又有一些差别
(boolean ) ;返回true或false
(and true) ;=> true
(and true true true) ;=> true
(and true true true true false) ;=> false
(and) ;=> true
;返回第一个非true值(nil或者false),或最后一个值
(or false false false false true) ;=> true
(or false false false) ;=> false
(or) ;=> nil
(not true) ;=> false
(not false) ;=> true
对于booleans, and, or 和 not 均接受非布尔值作为参数.
(记住false 、nil 逻辑上都是false,除此之外的其他任何东西逻辑上都是true.)
三、数据结构
似乎初步区分为Collections集合和Sequences序列两大类,Lists,Vectors,Sets,Maps等都属于Collections,很多操作符也都是通用的(还在学习中,对这块认知还不是很清晰)
列表Lists:(1 2 3)
数组Vectors:[1 2 3]
集合Sets:#{1 2 3} 集合特点是元素是唯一的,不会有重复
集合Sets的字面量表示方法:#{an-elem another-elem ...}
(set ["^^" "^^" "^__*__^"]) ;=> #{"^__*__^" "^^"}
(set [1 2 3 1 1 1 3 3 2 1]) ;=> #{1 2 3}
contains? 用来测试集合是否包含一个值
(conj set elem) 在集合中加入一个元素
(disj set elem) 在集合中移除一个元素
(clojure.set/union set1 set2 ...) 合并多个集合
(clojure.set/union #{1 2} #{2 3} #{1 2 3 4} #{7 8}) ;=> #{1 2 3 4 7 8}
(apply clojure.set/union [#{1 2} #{5} #{7 8}]) ;=> #{1 2 5 7 8}
字典Maps:{:a 1 :b 2 :c 3} {"foo" 42, "bar" 666} {"mehmeh" (+ 2 5) "rupatipor" "ropopo"}
在Maps里你需要分清楚key和keyword,map里面成对出现的第一个是key,key常用keyword关键字来作为key,但是key也可以不是关键字而是其他类型
;关键字keyword是以":"开头的字符,如 :title :authors
;关键字可以像函数一样用来索引查找关键字对应的值
如(:a {:a 1 :b 2 :c 3} ) ;=> 1
序列Sequences
(seq collection) ;把集合转变为序列
(first sequence) ;返回序列中第一个元素
(rest sequence) ;返回序列中第一个元素外的其余元素
(cons item sequence) ;返回一个新序列,item是整个序列的第一个元素,sequence是剩下的其余元素
get的用法:
(get ["a" "b" "c"] 15) ;=> nil
;超出index范围时不会报错而是返回nil,中间分隔符可以是空格也可以是",",第一个元素index是从0开始的,如想要“a“需要get 0
对maps用get,把后面的index序号替换为key:
(let [ages {"Juhana" 3
"Ilmari" 42
"King of All Cosmos" -6}]
(get ages "King of All Cosmos"))
;=> -6
;vectors是不可变的,你只能创建新的vector
(conj [1 2 3] 4) ;=> [1 2 3 4]
;assoc 有的替换,没有的新增
(assoc [1 2 3 4] 2 "foo") ;=> [1 2 "foo" 4] 这里把3替换成了”foo“
(assoc {:a 1} :b 2) ;=> {:b 2, :a 1}
(assoc {:a 1} :a 2) ;=> {:a 2}
;index: 0 1 2 0 1 2
(assoc [3 2 1] 1 "~o~") ;=> [3 "~o~" 1]
一些常用到的函数:
(str "Hello" 886 "!") ;连接字符串
(interpose ":" [1 2 3]) ;=> (1 ":" 2 ":" 3)
(interpose " and " ["a", "b"]) ;=> ("a" " and " "b")
(interpose ", " []) ;=> ()
(apply str (interpose " and " ["a", "b"])) ;=> "a and b"
(count [1 2 3]) ;=> 3
(count {:name "China Miéville", :birth-year 1972}) => 2 ;注意到字典maps里一对算一个元素
(count ":)") => 2
(filter pos? [-4 6 -2 7 -8 3]) ;filter返回一个指示符为true的序列
;=> ( 6 7 3 )
; value -4 6 -2 7 -8 3
; pos? false true false true false true
(filter (fn [x] (> (count x) 2)) ["ff" "f" "ffffff" "fff"])
;=> ("ffffff" "fff")
(take 20 (cycle ["foo" "bar"])) ;,cycle是虚幻,take取循环的前20次
(get "Clojure" 2) ;=> \o
(repeat 5 "*") ;=> ("*" "*" "*" "*" "*")
(repeat 3 "~o~") ;=> ("~o~" "~o~" "~o~")
一些指示符:
(number? n) returns true if n is a number.
(empty? coll) returns true if coll is empty.
(list? coll) and (vector? coll) test if coll is a list or a vector.
(count coll) returns the length of a list or a vector.
(contains? {"a" 1} "a") ;=> true
(contains? {"a" 1} 1) ;=> false ;注意到contains?用于字典maps时,只有包含key才返回true,并不匹配值
(contains? {"a" nil} "a") ;=> true
(contains? cities :title) ;=> true
(contains? cities :name) ;=> false
有点烧脑子的函数:
(map function collection)
;map有两个参数,第一个是函数,第二个是一个集合序列
;map会对集合中的每一个元素应用这个函数,并将求值后的结果返回成一个序列
(apply function a-seq)
;apply有两个参数,第一个是函数,第二个是序列,apply会对序列中的每一个值应用这个函数
;apply和map有什么区别,光看描述感觉两者很像,简单来说map返回的还是一个序列,apply则不是
(apply concat [["China Miéville"] ["Octavia E. Butler"]])
;=> (concat ["China Miéville"] ["Octavia E. Butler"])
;=> ("China Miéville" "Octavia E. Butler")
;(mapv) 和(filterv), map和filter返回值为数组Vectors而不是序列的另一个版本.
四、定义函数
(defn square [x]
"square a number"
( * x x))
;[ ]内的是参数,“ ”内描述函数功能,函数的返回值为最后一个表达式
let的用法:
(defn hypotenuse [x y]
(let [xx (* x x)
yy (* y y)]
(Math/sqrt (+ xx yy))))
(let [name1 value1
name2 value2
...]
(expression1)
(expression2)
...)
;let提供本地变量绑定,仅在let句式内生效
;let绑定的变量可以是在let里才刚刚声明的变量,但是有先后顺序的要求,即可以是a=1,b=a+1,但不能是a=b+1,b=1
(let [[x y z] [1 2 3 4 5 6]]
(str x y z))
;=> "123"
;let另一种绑定的对应方式
(defn sum-pairs [first-pair second-pair]
[(+ (first first-pair) (first second-pair))
(+ (second first-pair) (second second-pair))])
(defn sum-pairs [[x1 y1] [x2 y2]]
[(+ x1 x2) (+ y1 y2)])
(def x 1) ;绑定全局变量和值或函数
(if 判断条件 为真执行 为假执行) ;if后面的为判断条件,如果true执行第一个表达式,如果false执行第二个
(cond
condition1 true1
condition2 true2
condition3 true3
:else true4) ;cond用于分情况执行,如果condition1条件为false则跳到condition2,如果123都不是那么执行:else后面的
五、其他
linux安装lein用到的一些命令:
cd ~
mkdir bin
export PATH=$PATH:~/bin
source ~/.bashrc
chmod +x ~/bin/lein
lein常用命令:
lein midje :autotest 运行midje测试,在后面加 :autotest 可以在每次保存变更后自动运行
lein repl 打开Clojure repl循环
lein new 创建一个新的Clojure项目
lein help <command> 查找帮助
关于lein midje是如何测试的,是通过申明一系列的facts事实,然后测试函数运行结果与预期值是否相符
(facts "square"
(square 2) => 4
(square 3) => 9)
git常用命令:
git clone +链接 克隆文件到本地
git commit -a -m "message goes here" 把你的改动写入git关系,并在终端汇总显示你的改动
git push 上传你之前的改动到github需要账户及密码