这个系列还会继续的
忽然发现我之前已经建立了这篇博客,心想姑且把这篇完成吧,后续更高级的内容等我琢磨明白了再放出来。这个系列并不是一个单纯学习教程,更是我对Clojure认知的一种体现吧,说实话从java这种语言跳到clojure上还是有挺多问题的,至少思维方式可能就需要进行蛮大的转变,后续估计还会在读一些SICP之类的内容去逐渐加深自己对于LISP这个深坑的理解。
说一说常用的东西
这一节就来说一说常用的一些函数,在实际的应用中不论是刷题还是实际的开发应用,都会经常的用到,比如递归 map reduce apply 等等,这些都是接触clojure之后都会经常看到,用到的方法。那么,今天就来说说这些吧。
递归
递归的话在一般的语言中(没错,我说的就是Java),一般都会采用这种方式:
public int doMethod(int param){
// do something
doMethod(param);
}
在方法内部调用方法本身实现递归,完成方法的递归调用。
然而,在clojure中这种方式虽然可以,但却会造成堆栈的溢出(因为Clojure的尾递归优化不是用的原函数名,而是使用的recur,使用原函数名会不断产生新的实例从而造成堆栈溢出)。
(defn recur-fibo [n]
(letfn [(fib
[current next n]
(if (zero? n)
current
;recur将递归调用fib函数
(recur next (+ current next) (dec n))))]
(fib 0 1 n)))
上面这个例子摘自Clojure的recur尾递归优化探秘。文章主要表达的意思是,在方法实现方法实例后,每次刷新存储的参数,然后通过invoke调用方法实例实现尾递归优化。感兴趣的同学可以去仔细读一读庄晓丹大佬对于recur的解读,更为深入。
map reduce apply
map reduce apply都是遍历执行的方法,不过map、reduce和apply的返回值不太一样。
map
map返回的是一个lazy sequence,根据map的文档描述中可以知道,map是遍历所有传入参数,然后将每个参数执行传入函数的结果作为随后lazy-seq的元素。
(map #(+ 1 %) (range 10))
;; user=> (map #(+ 1 %) (range 10))
;; (1 2 3 4 5 6 7 8 9 10)
apply
apply的参数有两部分,一个是执行的函数,另一部分是执行函数的参数。
apply会依次传入参数并执行函数。
(apply + 1 [2 3 4 5])
(apply + [1 2 3 4 5])
;; user=> (apply + 1 [2 3 4 5])
;; 15
;; user=> (apply + [1 2 3 4 5])
;; 15
但是要注意,apply传入的参数必须能够是一个列表,否则会造成类型识别失败。
user=> (apply + 1 2 3 4 5)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:542)
reduce
reduce和apply类似,但是reduce中传入的函数中,函数的参数必须是两个。
(reduce + [1 2 3 4 5]) ;;=> 15
(reduce + []) ;;=> 0
(reduce + [1]) ;;=> 1
(reduce + [1 2]) ;;=> 3
(reduce + 1 []) ;;=> 1
(reduce + 1 [2 3]) ;;=> 6
(def x {:a 1 :b 2})
(reduce (fn [p [k v]]
(into p {k (+ 1 v)}))
{}
x)
;;=> {:a 2, :b 3}
结语
其实还有很多遍历的方法,比如在递归中用的很多的loop。还有for这个和java中的关键字长得很像的函数。但是由于我掌握的其实也很有限,在这一篇里就不再进行具体讲述了。在后续的章节里,我会逐渐加深内容的深度。这一篇就姑且到这里吧o( ̄▽ ̄)ブ