zoukankan      html  css  js  c++  java
  • Clojure常用模块

    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]
  • 相关阅读:
    Django视图
    Django模板系统
    错误:java.sql.SQLException: Access denied for user 'xxx'@'localhost' (using password: YES)
    关于Spring使用XML配置AOP时pointcut位置的一个小问题
    在jsp页面中将Java对象转换位JS对象的一个思路
    关于artifact XXXX:war exploded: Error during artifact deployment. See server log for details.错误
    框架集合——Java面向对象基础(33)
    使用Socket简单模拟C/S消息传递(UDP)——Java面向对象基础(32)
    使用Socket简单模拟C/S消息传递(TCP)——Java面向对象基础(31)
    对象序列化——Java面向对象基础(30)
  • 原文地址:https://www.cnblogs.com/fxjwind/p/3117544.html
Copyright © 2011-2022 走看看