最近看了一段clojure,下面是从书上摘下来的一下语言基础的精华部分
;函数的基本形式
(defn average [numbers] (/ (apply + numbers) (count numbers))) (average [60 80 100 400]) (read-string "42") (read-string "(+ 1 2)") (pr-str [1 2 3]) (read-string "[1 2 3]") "hello there" (class \c)
;;创建一份hashmap包含两个键值对
(def person {:name "Sandra Cruz" :city "Protland, ME"}) (:city person) person (:user/location person) (def x 1) x
;定义变量
(def x "hello") *ns* String Integer java.util.List java.net.Socket filter x
;quote阻止求值,‘x是其简便形式
(quote x) ‘x
;fn是依次行定义,所以只能定义的同时使用,不能分开使用,可以再使用def定义别名这样就可以多次使用了
(def strange-adder (fn adder-self-reference ([x] (adder-self-reference x 1)) ([x y] (+ x y)))) (strange-adder 10) ;defn=(def (fn )) (defn adder-self-reference1 ([x] (adder-self-reference1 x 1)) ([x y] (+ x y))) (adder-self-reference1 10) (adder-self-reference1 10 50)
;可变参函数,x是一个固定参数,剩下其余的参数都被解构到rest中
(defn concat-rest [x & rest] (apply str (butlast rest))) (concat-rest 0 1 2 3 4)
;fn定义后立即需要传入参数进行运算
((fn [x y] (Math/pow x y)) 2 10)
;使用#定义匿名函数
(#(Math/pow %1 %2) 2 10)
;匿名函数不隐含do,需要手工指定do
(#(do (println (str %1 \^ %2)) (Math/pow %1 %2)) 2 10)
;条件判断,第一个表达式是true的话,整个if就是第二个表达式的值,否则为第三个。任何非nil、非false的值都位true
(if (< 2 1) 2 1) (defn countdown [x] (if (zero? x) :blastoff! (do (println x) (recur (dec x))))) (countdown 0) (countdown 5)
;循环,recur能够在不消耗堆栈空间的情况下把程序执行转到离本地上下文最近的loop去
(loop [x 5];x=5 (if (neg? x) ;if x<0 x ;return x (recur (dec x)))) ;esle loop(x-1)
;高阶函数map,将函数作用在集合上,返回一个序列
(map clojure.string/lower-case ["Java" "Imperative" "Weeping" "Clojure" "Learning" "Peace"]) (map * [1 2 3 4] [5 6 7 8])
;高阶函数reduce 把集合应用在一个函数而产生单个值(归约)
(reduce max [0 -3 10 48]) (reduce + 50 [1 2 3 4]) (reduce (fn [m v] (assoc m v (* v v))) {} [1 2 3 4])
;偏函数,把函数的一部分参数传给一个函数,创建一个新函数,这个函数所需参数就是剩下的那部分参数
;Clojure使用partial提供偏函数
(def only-strings (partial filter string?)) (only-strings ["a" "b" 1 2 3])
;使用函数组合重写上面的函数,clojure使用comp来实现函数组合
;comp接受任意数量的函数,comp接收的参数与最后一个函数的参数个数相等,一次从最后一个函数往前调用,前一个函数的返回值是后一个函数的参数,如果不能作为后一个函数的参数,则会报错
;例,返回给定数字列表的所有数字总和的负数字符串形式
(defn negated-sum-str [& numbers] (str (- (apply + numbers)))) (negated-sum-str 10 12 3.4)
;函数组合的形式
(def negated-sum-str (comp str - +)) (negated-sum-str 10 12 3.4)
;编写高阶函数
;编写一个高阶函数。它返回某个给定数字与它的参数的和
(defn adder [n] (fn [x] (+ n x))) ((adder 5) 18)
;编写一个高阶函数,它接受一个函数作为参数,同时返回一个函数,返回函数的作用是返回接受的函数的返回值的2倍
(defn doubler [f] (fn [& args] (* 2 (apply f args)))) ((doubler +) 1 2 3)
;可以使用别名简化
(def double+ (doubler +)) (double+ 1 2 3)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;日志系统;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;定义输入转接 (defn print-logger [Writer];接收实现java.io.Writer接口的类实例 #(binding [*out* Writer];*out*默认绑定到标准输出,这里重新绑定到我们的Writer (println %)));把要打印的消息,用println写入到*out*(已经替换成了Writer) ;标准输出 (def *out*-logger (print-logger *out*)) (*out*-logger "hello") ;内存buffer (def writer (java.io.StringWriter.)) (def retained-logger (print-logger writer)) (retained-logger "hello") ;打印到文件 (require ‘clojure.java.io);require加载命名空间,可以起别名(require ‘clojure.java.io :as jio) (defn file-logger [file] #(with-open [f (clojure.java.io/writer file :append true)];flie以追加模式打开,并把这个writer局部命名为f;with-open能够保证f在with-open结束的时候被关闭 ((print-logger f) %))) (def log->file (file-logger "/home//breezeli/Document/messages.log"));不指定目录的话用light table就找不到存到哪个目录去了 (log->file "hello") ;记录到多处的日志串接,高阶函数 (defn mutli-logger [& logger-fns];任意数量参数 #(doseq [f logger-fns] (f %)));dosep遍历 (def log (mutli-logger (print-logger *out*) (file-logger "/home//breezeli/Document/messages.log"))) (log "hello again") ;每条日志前面加时间戳 (defn timestamped-logger [logger] #(logger (format "[%1$tY-%1$tm-%1$te %1$tH:%1$tM:%1$tS] %2$s"(java.util.Date.) %))) (def log-timestamped (timestamped-logger (mutli-logger (print-logger *out*) (file-logger "/home//breezeli/Document/messages.log")))) (log-timestamped "goodbye,now")
;;;;;;;;;;;;;;;END;;;;;;;;;;;;;;;;;;;
;没有副作用且调用成本很高的纯函数可以内存化加快处理速度,clojure使用memoize实现内存化
(defn prime? [n] (cond (== 1 n) false (== 2 n) true (even? n) false :else (->> (range 3 (inc (Math/sqrt n)) 2) (filter #(zero? (rem n %))) empty?)));检测给定数字是不是素数 (time (prime? 1125899906842679)) (let [m-prime? (memoize prime?)] (time (m-prime? 1125899906842679)) (time (m-prime? 1125899906842679)))
;map、vector、set和列表是Clojure提供的基本数据解构
‘(a b :name 12.5) ;;列表 [‘a ‘b :name 12.5] ;;vector {:name "Chas" :age 31} ;;map #{1 2 3} ;;set
;Clojure中所有的数据结构都实现了Collection抽象
;Collection的提供一下核心的集合函数
;conj添加一个元素到集合,保证对于所有的集合类型,都会高效地把元素添加到列表的第一个位置,因为如果添加在最后的话需要遍历列表,无法保证高效
;seq获取集合的顺序视图
;into建立在conj和seq之上
;count获取集合的元素个数
;empty获取一个跟所提供集合类型一样的空集合
;=判断两个或多个集合是否相等
;sequence系列,通常都被叫做“seq”,除了支持Collection提供的函数外还支持
;seq返回给传入参数的一个序列
(seq "Clojure")
;first、rest、next提供遍历序列的方法
(first "Clojure")
(rest "Clojure")
(next "Clojure") ;如果操作的结果是空,rest始终返回一个空序列,next返回nil,这是rest和next唯一的区别
;lazy-seq创建一个内容是一个表达式结果的惰性序列