这个系列还会继续的

忽然发现我之前已经建立了这篇博客,心想姑且把这篇完成吧,后续更高级的内容等我琢磨明白了再放出来。这个系列并不是一个单纯学习教程,更是我对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( ̄▽ ̄)ブ