Clojure常用模块

2019-03-02 23:51|来源: 网路

http://qiujj.com/static/clojure-handbook.html

http://clojure.github.io/clojure/

Base

 

->, (-> x form & more)

http://clojuredocs.org/clojure_core/clojure.core/-%3E

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

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

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

;; Arguably a bit cumbersome to read:
user=> (first (.split (.replace (.toUpperCase "a b c d")
                                "A"   "X")
                      " "))
"X"

;; Perhaps easier to read:
user=> (-> "a b c d" 
           .toUpperCase 
           (.replace "A" "X") 
           (.split " ") 
           first)
"X"

->> , (->> x form & more)

http://clojuredocs.org/clojure_core/clojure.core/-%3E%3E

Inserts x as the last item in the first form

和->的差别在于x插入的位置不同,
->是插入在第二个item, 即紧跟在函数名后面,
而->>是插在最后一个item

;; An example of using the "thread-last" macro to get
;; the sum of the first 10 even squares.
user=> (->> (range)
            (map #(* % %))
            (filter even?)
            (take 10)
            (reduce +))
1140

;; This expands to:
user=> (reduce +
               (take 10
                     (filter even?
                             (map #(* % %)
                                  (range)))))
1140

 

comp, (comp f1 f2 f3 & fs)

以一组函数为参数, 返回一个函数, 如例子my-fn
使用my-fn的效果就是,
my-fn的参数个数等于fs所需的参数个数, 因为实际做法就是拿my-fn的参数调用fs, 然后用fs的返回值调用f3…一直继续
所以除了fs以外的函数, 都必须只包含一个参数, 所以经常使用partial来减少参数个数, 配合使用

user=> (def my-fn (comp (partial * 10) - *))
user=> (my-fn 5 3) ;  10*(-(5*3))
-150

 

if-let, when-let

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

user=> (defn sum-even-numbers [nums]
         (if-let [nums (seq (filter even? nums))]
           (reduce + nums)
           "No even numbers found."))
user=> (sum-even-numbers [1 3 5 7 9])
"No even numbers found."
user=> (sum-even-numbers [1 3 5 7 9 10 12])
22

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

(defn drop-one
  [coll]
  (when-let [s (seq coll)]
    (rest s)))
user=> (drop-one [1 2 3])
(2 3)
user=> (drop-one [])
nil

 

cond, condp, case

cond, 替代多个if

(cond (< 0 n ) "n>0"
      (< 10 n) "n>10"
      :else "n <=0") ;;:else只是习惯写法, 任意true都可以
   

condp, 简化cond, <n只需要写一遍
默认会将,0,10作为, 函数<的第一个参数, 即(< 0 n), (< 10 n)
最后一行默认为:else

(condp < n
       0    "n>0"
       10  "n>10"
       "n<=0")

 

case, 支持多选或不同类型

(case x
    1 10
    2 20
    3 30
    0)

(case x
    (5 10) "*5"
    (3 6 9) "*3"
    "others")

(case x
    "JoC"   :a-book
    :eggs   :breakfast
    42      (+ x 100)
    [42]    :a-vector-of-42
    "The default")

 

defnk

和普通defn的不同是, 可以在参数里面使用k,v, 并且可以在函数体中直接使用k来得到value
其实它的实现就是增加一个hashmap来存放这些k,v

user> (use 'clojure.contrib.def)
nil
user> (defnk f [:b 43] (inc b))
#'user/f
user> (f)
44
user> (f :b 100)
101
user=> (defnk with-positional [foo :b 43] (+ foo (inc b)))
#'user/with-positional
user=> (with-positional 5 :b 1)
7

 

Collection操作

'(a b :name 12.5) ;; list
['a 'b :name 12.5] ;; vector
{:name "Chas" :age 31} ;; map
#{1 2 3} ;; set 

General

(first '(:alpha :bravo :charlie)) ;;:alpha
(rest [1 2 3 4 5]) ;;(2 3 4 5),无论输入,都是返回seq
(rest [1]) ;;(),返回空seq, 而next会返回nil
(cons 1 '(2 3 4 5 6)) ;;(1 2 3 4 5 6), (cons x seq), 将单个x加入seq, 多用conj代替 (conj [:a :b :c] :d :e :f :g) ;;[:a :b :c :d :e :f :g],将后面多个elem逐个加入col
(seq {:a 5 :b 6}) ;;([:a 5] [:b 6]), 将各种coll转化为seq
(count [1 2 3]) ;;= 3
(reverse [1 2 3 4]) ;;(4 3 2 1)
(interleave [:a :b :c] [1 2]) ;;(:a 1 :b 2)

 

(every? empty? ["" [] () '() {} #{} nil]) ;;true, 判断是否为空
(map empty [[\a \b] {1 2} (range 4)]) ;;([] {} ()), 清空

(def not-empty? (complement empty?)) ;;(complement f), (not-empty? []) –> false, 取反

 

(range start? end step?)
(range 10) ;;(0 1 2 3 4 5 6 7 8 9)
(range 1 25 2)  ;;(1 3 5 7 9 11 13 15 17 19 21 23)
(repeat 5 1) ;;(1 1 1 1 1)
(take 10 (iterate inc 1)) ;;(1 2 3 4 5 6 7 8 9 10), iterate和cycle都是返回无限队列, 所以需要take
(take 10 (cycle (range 3))) ;;(0 1 2 0 1 2 0 1 2 0)

 

(group-by count ["a" "as" "asd" "aa" "asdf" "qwer"]) ;;{1 ["a"], 2 ["as" "aa"], 3 ["asd"], 4 ["asdf" "qwer"]}, group-by f coll
(sort > [42 1 7 11]), (42 11 7 1) ;;默认是升序, 这里改成降序
(sort-by #(.toString %) [42 1 7 11]) ;;按str比较,所以7>42,(1 11 42 7)
(filter even? (1 2 3 4 5 6)) ;;(2 4 6)
 
(split-at 2 [1 2 3 4 5]) ;;[(1 2) (3 4 5)], (split-at n coll)
(split-with (partial >= 3) [1 2 3 4 5]) ;;[(1 2 3) (4 5)], (split-with pred coll), 在第一个不满足pred的地方split
(partition 4 [0 1 2 3 4 5 6 7 8 9]) ;;((0 1 2 3) (4 5 6 7)) (partition-all 4 [0 1 2 3 4 5 6 7 8 9]) ;;((0 1 2 3) (4 5 6 7) (8 9)) ;;lazy,并不去尾
(partition 4 2 "pad" (range 10)) ;;((0 1 2 3) (2 3 4 5) (4 5 6 7) (6 7 8 9) (8 9 \p \a)), 加上step和pad

 

Set

(union #{1 2} #{2 3}) ;;#{1 2 3}
(intersection #{1 2} #{2 3}) ;;#{2}
(difference #{1 2} #{2 3}) ;;#{1}
(disj #{1 2 3} 3 1) ;;#{2}, 删除

 

Vector

(nth [:a :b :c] 3) ;;= java.lang.IndexOutOfBoundsException, 等于([:a :b :c] 3)
(get [:a :b :c] 3) ;;nil,和nth的不同

stack
clojure中需要注意,
list, 是stack逻辑(LIFO), 而vector是queue的逻辑(FIFO)

(conj [] 1 2 3) ;[1 2 3]
(conj '() 1 2 3) ;(3 2 1)
(first (conj '() 1 2 3)) ;3
(first (conj [] 1 2 3)) ;1

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

(pop (conj [] 1 2 3)) ;[1 2], 和rest不同
(peek (conj [] 1 2 3)) ;3, 和first不同

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

 

Hashmap

(assoc map key val) ;;add kv
(dissoc map key) ;;remove kv
(keys {:sundance "spaniel", :darwin "beagle"}) ;;(:sundance :darwin)
(vals {:sundance "spaniel", :darwin "beagle"}) ;;("spaniel" "beagle")
(get {:sundance "spaniel", :darwin "beagle"} :darwin) ;; "beagle"
(select-keys map keyseq) ;;get多个key, (select-keys {:a 1 :b 2} [:a :c]) {:a 1}

 

into, (into to from)

把from join到to, 可以看到底下对于list, vector, set, 加完的顺序是不同的, 刚开始有些疑惑
其实Into, 只是依次从from中把item读出, 并append到to里面, 最终的顺序不同因为数据结构对append的处理不同

; Adds a list to beginning of another.  Note that elements of list are added in reverse since each is processed sequentially.
(into '(1 2 3) '(4 5 6))
=> (6 5 4 1 2 3)
(into [5 6 7 8] '(1 2 3 4))
=> [5 6 7 8 1 2 3 4]        
(into #{5 6 7 8} [1 2 3 4])
=> #{1 2 3 4 5 6 7 8}  

merge, (merge & maps)

把多个map merge在一起, 如果有一样的key则latter优先原则, 后出现的优先
user=> (merge {:a 1 :b 2 :c 3} {:b 9 :d 4})
{:d 4, :a 1, :b 9, :c 3} 

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

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

;; merge two maps using the addition function
user=> (merge-with + 
                   {:a 1  :b 2}
                   {:a 9  :b 98 :c 0})    
{:c 0, :a 10, :b 100}

 

apply, map, reduce, for

apply, (apply f args)

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

(apply f e1 [e2 e3]) ;; (f e1 e2 e3)
(apply max [1 3 2]) ;; (max 1 3 2)

(apply + 1 2 '(3 4))  ;; (+ 1 2 3 4))

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 & colls)

和普通map不同的是, 会对map执行的结果执行concat操作
等于(apply concat (map f &colls)) ;;注意apply的作用

user=> (mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]])
(0 1 2 3 4 5 6 7 8 9)

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的话, 会穷尽组合

user=> (for [x [0 1 2 3 4 5]
             :let [y (* x 3)]
             :when (even? y)]
            y)
(0 6 12)
 
user=> (for [x ['a 'b 'c] 
             y [1 2 3]] 
          [x y])

([a 1] [a 2] [a 3] [b 1] [b 2] [b 3] [c 1] [c 2] [c 3])

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

 

doall, dorun

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

doall会hold head, 并返回整个seq, 所以过程中seq保存在memory中, 注意outofmemory
dorun不会hold head, 遍历run, 最终返回nil

(doall (map println [1 2 3]))
1
2
3
(nil nil nil)

(dorun (map println [1 2 3]))
1
2
3
nil 

doseq

doseq, 其实就是支持dorun的for(list comprehension), 和for语法基本一致
for返回的是lazy-seq, 而doseq = dorun (for…)

(doseq [x (range 7) y (range x) :while (odd? x)] (print [x y]))
(for [x (range 7) y (range x) :while (odd? x)] [x y])

user=> (doseq [x [1 2 3] 
               y [1 2 3]] 
         (prn (* x y)))
1
2
3
2
4
6
3
6
9
nil

 

并发STM

ref, 多个状态的协同更新(transaction)

(def v1 (ref 10))
(deref v1) ;;@v1
(dosync (ref-set v1 0)) ;;update
(dosync (ref-set v1 (inc @v1)))
(dosync (alter v1 inc)) ;;alter, read-and-set,后面跟函数
(dosync (alter v1 + 10))

atom, 单个状态的非协同更新

(def v1 (atom 10))
(reset! v1 20) ; @v1=20 ;;单个值,所以不需要dosync来保证transaction
(swap! v1 + 3) ; @v1=23 ;;read-and-set

(def v2 (atom {:name "qh" :age 30}))
(swap! v2 assoc :age 25) ; @v2={:name "james" :age 25

 

Java调用

先看下最常用的对应表,

image

 image

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

(instance? String "foo") ;;true

(defn length-of [^String text] (.length text)) ;;Type Hinting

 

gen-class

http://clojure.org/compilation

解决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)

(ns clojure.examples.hello
    (:gen-class))

在Storm里面的例子, DefaultScheduler实现接口IScheduler, 接口实现函数有'-'前缀, 如'-schedule’

(ns backtype.storm.scheduler.DefaultScheduler  
  (:gen-class
    :implements [backtype.storm.scheduler.IScheduler]))
(defn -prepare [this conf]
  )
(defn -schedule [this ^Topologies topologies ^Cluster cluster]
  (default-schedule topologies cluster))

 

memfn, (memfn name & args)

Java中, 方法调用, file.isDirectory()
但对于clojure, 函数是first class, 所以调用方式为isDirectory(file)

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

user=> (def *files* (file-seq (java.io.File. "/tmp/")))
user=> (count (filter (memfn isDirectory) *files*))
68
user=> (count (filter #(.isDirectory %) *files*))
68

可以看到其实是调用*files*.isDirectory(), 但通过memfn, 看上去好像是使用isDirectory(*files*)
直接看下这个macro的实现, 就是把memfn(name, args)转化为target.name(args)

(defmacro memfn
  "Expands into code that creates a fn that expects to be passed an
  object and any args and calls the named instance method on the
  object passing the args. Use when you want to treat a Java method as
  a first-class fn."
  {:added "1.0"}
  [name & args]
  `(fn [target# ~@args]
     (. target# (~name ~@args))))

 

satisfies? , (satisfies? protocol x)

Returns true if x satisfies the protocol, 其实就是判断x是否实现了protocol
如下列, number只extend了protocol Bar, 而没有extend Foo

(defprotocol Foo
  (foo [this]))
(defprotocol Bar
  (bar [this]))
(extend java.lang.Number
  Bar
  {:bar (fn [this] 42)})

(satisfies? Foo 123)   ; => false
(satisfies? Bar 123)   ; => true

 

Test&Logging

Test

两种形式,

deftest

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

(deftest test-foo
   (is (= 1 2)))
(test-foo) ;;nil, pass没有输出

(deftest test-foo
   (is (= 1 2)))
(test-foo) ;;fail
FAIL in (test-foo) (NO_SOURCE_FILE:2)
expected: (= 1 2)
  actual: (not (= 1 2))

with-test

这种方法, 把testcase加在metadata里面, 类似python的doctest
不影响函数的正常使用, 如下

(with-test
  (defn hello [name]
    (str "Hello, " name))
  (is (= (hello "Brian") "Hello, Brian"))
  (is (= (hello nil) "Hello, nil")))

(hello "Judy") ;;"Hello, Judy"
((:test (meta #'hello)))
FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:5)
expected: (= (hello nil) "Hello, nil")
  actual: (not (= "Hello, " "Hello, nil"))
false

Logging

Clojure世界:日志管理——clojure.tools.logging

(ns example.core
  (:use [clojure.tools.logging :only (info error)]))
(defn divide [x y]
  (try
    (info "dividing" x "by" y)
    (/ x y)
    (catch Exception ex (error ex "There was an error in calculation"))))

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

(log-message "test," (pr-str '(1 2 3 4 5)))

pr-str

user=> (def x [1 2 3 4 5])
;; Turn that data into a string...
user=> (pr-str x)
"[1 2 3 4 5]"
;; ...and turn that string back into data!
user=> (read-string (pr-str x))
[1 2 3 4 5]

转自:http://www.cnblogs.com/fxjwind/archive/2013/06/04/3117544

相关问答

更多
  • 本人目前就职于大连的软件公司,从事java web开发。框架就用到了spring。最最重要和常用的就是IOC了。而AOP直接应用很少,但是spring的事务管理是通过AOP完成的,好好理解下AOP对于以后学习还是很有帮助的。 其他的都可以先不看,精力有限,看更加重要的东西,软件行业的二八法则你应该知道吧?技术中只有20%是最常用和最关键的,呵呵 祝你成功,加油吧~~~
  • 安装Python包,pip最为方便了,简单快捷,因为它直接是从pypi上面下载文件,保证文件的安全性和可靠性,并且资源丰富,下面是安装步骤: 1. 下载 setuptools,注意对应 Python 的版本,完成后运行 exe 即可完成安装 https://pypi.python.org/pypi/setuptools/1.4.1 setuptools-1.4.1.win32-py2.6.exe 2. 下载 pip https://pypi.python.org/pypi/pip pip-1.4.1.tar ...
  • 几种情况 1.预编译好的exe文件,直接双击安装即可 2.rar等包,解压后,从命令行中cd到解压的目录,通过setup.py文件来安装模块: 命令为,python setup.py install (若python未配置环境变量,则需要给python的安装绝对路径,如: C:/python27/python setup.py install) 备注:一般第三方模块安装好后,在 python安装路径/Lib/site-package下可以看得到
  • 一、导入模块 1. import module 2. from module.xx import xx 3. from module.xx import xx as rename 4. from module.xx import * 二、开源模块 1. yum、pip、apt-get 2. 源码编译安装:Python setup.py build Python setup install 三、常用模块 1. os模块#用作系统级别的工作 2. sys模块#提供解释器相关操作 3. hashlib模块# 用于 ...
  • tcpdump就可以啊~
  • 例如安装 openpyxl 这个模块 windows上: 在cmd中输入: pip install openpyxl 就是pip install 后加模块名 OS X 和 Linux 上,用 pip3 代替 pip 即可。
  • 尝试打开你的project.clj 。 将出现一个提示,说Leiningen项目没有注册。 按Add project ,然后继续Run/debug configurations > Edit configurations > Add Clojure REPL > Local Try to open your project.clj. A hint shall appear, saying, Leiningen project is not registered. Press Add project and ...
  • 首先,很难准确地说出你想要做什么。 在处理编程问题时,它可以帮助您和其他人帮助您拥有一个“小案例”,您可以在展开并解决更大问题之前解决问题。 从听起来来看,您试图从某些元素中提取内容并根据该内容执行操作。 我整理了一个带有一些简单内容的小型XML文件,以便尝试: data else
  • 您可以通过将其信息添加到您的project.clj文件中的依赖项来安装一个contrib模块。 当你下次运行lein ,它会注意到你的改变并自动为你抓取图书馆。 我在这里写了一些更详细的说明 。 You install a contrib module by adding its info to :dependencies in your project.clj file. The next time you run lein for something, it notices your change an ...
  • 你可以通过打开Clojure代码来找到返回惰性序列的函数https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj并搜索“返回一个懒惰” 我不知道他们的任何策划清单。 经验法则是:如果它返回一个序列,它将是一个惰性序列,如果它返回一个值,它将强制评估。 当使用新的函数,宏或特殊形式时,请阅读文档字符串。 大多数开发环境都有一个显示文档字符串的关键字,或者至少导航到源文件(您可以在其中查看文档字符串),并且总是存在http:/ ...