天天看点

Clojure常用模块

->, (-> x form & more)

<a href="http://clojuredocs.org/clojure_core/clojure.core/-%3e">http://clojuredocs.org/clojure_core/clojure.core/-%3e</a>

线性化嵌套, 使其更具有可读性,  inserts x as the second item in the first form

从下面的例子可以看出, 就是把第一个参数(x)作为最初的输入, 调用第二个参数(代表的fn), 然后拿返回值调用后续函数

和..用处差不多, 但..只能用于java调用

-&gt;&gt; , (-&gt;&gt; x form &amp; more)

<a href="http://clojuredocs.org/clojure_core/clojure.core/-%3e%3e">http://clojuredocs.org/clojure_core/clojure.core/-%3e%3e</a>

inserts x as the last item in the first form

和-&gt;的差别在于x插入的位置不同, 

-&gt;是插入在第二个item, 即紧跟在函数名后面, 

而-&gt;&gt;是插在最后一个item

<code></code>

comp, (comp f1 f2 f3 &amp; fs)

以一组函数为参数, 返回一个函数, 如例子my-fn 

使用my-fn的效果就是, 

my-fn的参数个数等于fs所需的参数个数, 因为实际做法就是拿my-fn的参数调用fs, 然后用fs的返回值调用f3…一直继续 

所以除了fs以外的函数, 都必须只包含一个参数, 所以经常使用partial来减少参数个数, 配合使用

if-let, when-let

对let添加if判断, 如下面的例子, 如果nums非false或nil, 则执行累加, 否则表示list中没有偶数打印"no even numbers found." 

适用于对于不同的let结果的不同处理

when-let, 一样的理论, 当let赋值非false或nil时, 执行相应逻辑, 否则返回nil

cond, condp, case

cond, 替代多个if

condp, 简化cond, &lt;n只需要写一遍 

默认会将,0,10作为, 函数&lt;的第一个参数, 即(&lt; 0 n), (&lt; 10 n) 

最后一行默认为:else

case, 支持多选或不同类型

defnk

和普通defn的不同是, 可以在参数里面使用k,v, 并且可以在函数体中直接使用k来得到value 

其实它的实现就是增加一个hashmap来存放这些k,v

stack 

clojure中需要注意, 

list, 是stack逻辑(lifo), 而vector是queue的逻辑(fifo)

但是也可以让vector, 表现出stack逻辑, 用pop和peek

对于list, peek和pop就等同于first,rest

into, (into to from)

把from join到to, 可以看到底下对于list, vector, set, 加完的顺序是不同的, 刚开始有些疑惑 

其实into, 只是依次从from中把item读出, 并append到to里面, 最终的顺序不同因为数据结构对append的处理不同

merge, (merge &amp; maps)

把多个map merge在一起, 如果有一样的key则latter优先原则, 后出现的优先 

merge-with, (merge-with f &amp; maps)

普通merge只是val的替换, 而merge-with可以使用f来merge, 比如下面的例子就是用+

apply, (apply f args)

作用就是将args作为f的参数, 并且如果有collection, 会将elem取出作为参数

<code>(</code><code>apply</code> <code>+</code> <code>1</code> <code>2</code> <code>'(</code><code>3</code> <code>4</code><code>)</code><code>)</code>  <code>;; (+ 1 2 3 4))</code>

map,

(map f [a1 a2..an])  ;; ((f a1) (f a2) .. (f an))

(map f [a1 a2..an] [b1 b2..bn] [c1 c2..cn])  ;; ((f a1 b1 c1) (f a2 b2 c2) .. (f an bn cn))

mapcat, (mapcat f &amp; colls)

和普通map不同的是, 会对map执行的结果执行concat操作 

等于(apply concat (map f &amp;colls)) ;;注意apply的作用

reduce, (reduce f coll) or (reduce f val coll)

(reduce f [a b c d ... z])

(reduce f a [b c d ... z])

就是:

    (f (f .. (f (f (f a b) c) d) ... y) z)

和apply的不同,

(reduce + [1 2 4 5]) ;; (+ (+ (+ 1 2) 4) 5)

(apply + [1 2 4 5]) ;; (+ 1 2 4 5)

for, (for seq-exprs body-expr)

for, 类似于python的list comps, 用于简化map, filter 

两部分, 

第一部分是seq-exprs, 列出lazy seq, 并且后面可以跟:let, :when, :while等定义和条件, 如下面的例子 

第二部分是body-expr, 取出前面定义的lazy seq的每个元素执行body-expr, for返回的就是所有元素执行结果的list, 参考下例, 如果有多个lazy seq的话, 会穷尽组合

但是需要注意的是, for返回的只是lazy seq, 所以如果需要确保body-expr在每个元素上都得到执行, 必须加dorun或doall

doall和dorun都用于force lazy-seq, 区别在于

doall会hold head, 并返回整个seq, 所以过程中seq保存在memory中, 注意outofmemory 

dorun不会hold head, 遍历run, 最终返回nil

doseq, 其实就是支持dorun的for(list comprehension), 和for语法基本一致 

for返回的是lazy-seq, 而doseq = dorun (for…)

先看下最常用的对应表,

Clojure常用模块
Clojure常用模块

(class "foo") ;;java.lang.string

(instance? string "foo") ;;true

(defn length-of [^string text] (.length text)) ;;type hinting

<a href="http://clojure.org/compilation">http://clojure.org/compilation</a>

解决compile ahead-of-time (aot)问题, clojure作为动态语言, 会在runtime的时候compile并跑在jvm上, 但是某些时候需要提前compile并产生class 

比如, deliver时没有源码, 或希望你的clojure代码可以被java调用...

clojure compiles all code you load on-the-fly into jvm bytecode, but sometimes it is advantageous to compile ahead-of-time (aot). some reasons to use aot compilation are: to deliver your application without source to speed up application startup to generate named classes for use by java to create an application that does not need runtime bytecode generation and custom classloaders

解决这个问题的方法就是使用gen-class, 往往配合ns使用, 这样会自动为该namespace生成class(省去:name)

在storm里面的例子, defaultscheduler实现接口ischeduler, 接口实现函数有'-'前缀, 如'-schedule’

java中, 方法调用, file.isdirectory() 

但对于clojure, 函数是first class, 所以调用方式为isdirectory(file)

问题是, 我在clojure里面使用java类函数时, 也想以first class的方式, 那么就需要memfn来转化

可以看到其实是调用*files*.isdirectory(), 但通过memfn, 看上去好像是使用isdirectory(*files*) 

直接看下这个macro的实现, 就是把memfn(name, args)转化为target.name(args)

returns true if x satisfies the protocol, 其实就是判断x是否实现了protocol 

如下列, number只extend了protocol bar, 而没有extend foo

两种形式,

deftest

其实就是创建函数, 象普通函数一样去调用定义的test fn

with-test

这种方法, 把testcase加在metadata里面, 类似python的doctest 

不影响函数的正常使用, 如下

<a href="http://www.blogjava.net/killme2008/archive/2012/02/12/369822.html">clojure世界:日志管理——clojure.tools.logging</a>

storm里面对其进行了封装, backtype.storm.log

pr-str