内容简介:Unknown perls from the Clojure standard library 筆記
看完 ClojuTRE 2015 的 Unknown pearls from the Clojure standard library - Renzo Borgatti 演講後,來紀錄個筆記。
這場演講介紹了一些在 clojure.core 裡面的函式,這些函式平常可能不太有機會用到,但是可以協助我們除錯程式的問題。
投影片: 下載連結
destructure
destructure (解構) 在 Clojure 裡面是個非常實用的功能,可以方便我們對資料直接指派變數去代替它,如下:
No destructuring
(let [data [1 2 3]
a (nth data 0)
b (nth data 1)
c (nth data 2)]
(println "a:" a "b:" b "c:" c))
;; => a: 1 b: 2 c: 3
With destructuring
(let [[a b c] [1 2 3]] (println "a:" a "b:" b "c:" c)) ;; => a: 1 b: 2 c: 3
我們可以透過 destructure 去觀察一個東西是如何被解構的,這邊是投影片給的範例:
(destructure '[[x y & others] v]) ;; => [v2 v ;; x (nth v2 0 nil) ;; y (nth v2 1 nil) ;; others (nthnext v2 2)]
當然投影片給的是整理後的結果,實際上我執行得到的結果是這樣的
[vec__23596 v seq__23597 (clojure.core/seq vec__23596) first__23598 (clojure.core/first seq__23597) seq__23597 (clojure.core/next seq__23597) x first__23598 first__23598 (clojure.core/first seq__23597) seq__23597 (clojure.core/next seq__23597) y first__23598 others seq__23597]
reductions
我們在 Clojure 中很常用 reduce 去將一個函數作用到 list 上的每兩個元素上,然後返回最後的結果,最常見的簡單函數就是一個 list 的元素全部相加
(reduce + (range 10)) ;; => 45
而 reduce 的運作過程,則可以透過 reductions 來協助我們進行查看,可以看到這邊最後得到的 45 就是我們想要的結果。
(reductions + (range 10)) ;; => (0 1 3 6 10 15 21 28 36 45)
test
在 Clojure 中,我們可以在 metadata 中設定好對一個函數的測試方式,然後呼叫 test 對該函數進行測試,這項功能很適合用在小函式的一些 assertion 測試上。
(defn add+
{:test #(do
(assert (= (add+ 2 3) 5))
(assert (= (add+ 4 4) 8)))}
[x y] (+ x y))
(test #'add+) ;; <= trigger
;; => :ok
你也可以透過 meta 去查看你這個函式的 metadata 或是測試用的函式資訊
(meta #'addd+)
;; =>
{:arglists ([x y]),
:test #function[hello.core/fn--23678],
:line 350,
:column 4,
:file "/home/coldnew/Workspace/hello/src/hello/cpre.clj",
:name add+,
:ns #namespace[hello.core]}
clojure.pprint/cl-format
clojure.pprint/cl-format 是 Clojure 移植 Common Lisp 的 format 函式,對於同時寫 Clojure 和 ClojureScript 的開發者而言, cl-format 可以同時用於 Clojure 和 ClojureScript 上,方便了不少。
(註: CLJS-324 ISSUE 尚未被解決前,Clojure 的 format 是無法用於 ClojureScript 上的)
如果要更多關於 cl-format 的使用,可以看看 Praticle Common Lisp 一書,我在 clojure/clojurescript 與 left-pad 一文亦有提到如何透過 cl-format 實作 Clojure/ClojureScript 皆可以用的 leftpad 函式。
投影片上給的範例則是這樣:
(clojure.pprint/cl-format nil "~:r" 1234) ;; => one thousand, two hundred thirty-fourth (clojure.pprint/cl-format nil "~@r" 1234) ;; => MCCXXXIV
clojure.java.browse/browse-url
clojure.java.browse/browse-url 會呼叫系統預設的瀏覽器,開啟你所指定的網頁。
(clojure.java.browse/browse-url "http://localhost:3000")
clojure.java.javadoc/javadoc
Clojure 畢竟是 JVM 上的語言,有時候我們需要查看一些 javadoc,或是查看 Clojure 內部的 Java 實現,可以透過 clojure.java.javadoc/javadoc 來查看
(clojure.java.javadoc/javadoc (list* 1 [])) ;; => open clojure.lang.Cons Javadoc
clojure.reflect/reflect
老實說我看了還是不知道這是什麼,也許是和 Java 的 reflection 有關,不過我們還是可以在 clojure.reflect/reflect 的文檔中看出一些東西
(require '[clojure.reflect :refer [reflect]])
(require '[clojure.pprint :refer [print-table]])
;; Here we have a simple function that prints the
;; important bits of the class definition in a table.
(->> String
reflect
:members
print-table)
;; =>
;; | :name | :return-type | :declaring-class | :parameter-types | :exception-types | :flags |
;; |--------------------------+------------------------+------------------+--------------------------------------------------------+----------------------------------------+-------------------------------|
;; | replaceAll | java.lang.String | java.lang.String | [java.lang.String java.lang.String] | [] | #{:public} |
;; | CASE_INSENSITIVE_ORDER | | java.lang.String | | | #{:public :static :final} |
;; | indexOf | int | java.lang.String | [char<> int int char<> int int int] | [] | #{:static} |
;; | codePointCount | int | java.lang.String | [int int] | [] | #{:public} |
;; | getChars | void | java.lang.String | [int int char<> int] | [] | #{:public} |
;; | regionMatches | boolean | java.lang.String | [int java.lang.String int int] | [] | #{:public} |
;; | isEmpty | boolean | java.lang.String | [] | [] | #{:public} |
;; | codePointAt | int | java.lang.String | [int] | [] | #{:public} |
;; | lastIndexOf | int | java.lang.String | [java.lang.String] | [] | #{:public} |
;; | startsWith | boolean | java.lang.String | [java.lang.String int] | [] | #{:public} |
;; ...etc
講者在投影片中給的範例則是這個: (注意到結果是節錄呦~)
(require '[clojure.reflect :refer [reflect]])
(println (with-out-str (clojure.pprint/write (reflect :a))))
;; extract from a typical output:
{:name invoke,
:return-type java.lang.Object,
:declaring-class clojure.lang.Keyword,
:parameter-types [java.lang.Object java.lang.Object],
:exception-types [],
:flags #{:public :final}}
clojure.inspector/inspect-tree
我們在處理一些樹狀資料時(ex: JSON 格式),有個圖示化的 工具 可以方便瀏覽所有資訊,這邊可以透過 clojure.inspector/inspect-tree 來查看
(require '[clojure.inspector :as i])
(def m {:a "a"
:b {:c "c"
:d [1 2 3]
:e {:f "f"
:g "g"
:h "h"}}
:i [1 2 3]
:l {:m "m"
:n "n"}
:o [{:p "p" :q "q"}]})
(i/inspect-tree m)
這樣呼叫,會得到如下的視窗
clojure.lang.PersistentQueue
clojure.lang.persistentQueue 是 Clojure 下並未寫在 doc 上面的 Queue 實現 (java) ,我們可以用它來實作我們需要的 queue (佇列) 功能
基本上,你有以下幾種方法可以對你的 PersistentQueue 進行處理
-
peek
取得 queue 最頂端的資料(head)
-
pop
回傳不包含最頂端資料(head)的一個新的 PersistentQueue
-
conj
將資料加入到 queue 的尾巴
-
empty?
檢測 queue 是否為空的
-
seq
將 queue 的資料變成序列 (sequence)
投影片上,講者是舉這範例:
(def e (clojure.lang.PersistentQueue/EMPTY)) (def buf (reduce conj e (range 10))) (peek buf) ;; => 0 (peek (pop buf)) ;; => 1 (peek (pop (pop buf))) ;; => 2
而 StackOverflow 則是有人舉出了如何透過 clojure.lang.persistentQueue 實現自己的 queue 函式
(defn queue
([] clojure.lang.PersistentQueue/EMPTY)
([coll] (reduce conj clojure.lang.PersistentQueue/EMPTY coll)))
(defmethod print-method clojure.lang.PersistentQueue
[q ^java.io.Writer w]
(.write w "#queue ")
(print-method (sequence q) w))
(comment
(let [*data-readers* {'queue #'queue}]
(read-string (pr-str (queue [1 2 3])))))
fnil
我們在用 Clojure 處理東西的時候,有時候可能是資料本身就是 nil 的情況,這種狀況下對 nil 進行處理可能就會導致 Exception 的狀況發生
在傳統的 LISP 中,常以 or 作為一個保護,一旦遇到 nil 的情況,則回傳預設值
(or nil 10) ;; => 10
fnil 則是用來替你的函式多加一層保護,讓函式遇到 nil 的狀況可以避免一些 Exception 的發生
講者在投影片上提供的範例是這樣的:
(def m {:host "127.0.0.1" :port nil})
(update m :port (fnil #(Integer/parseInt %) "80"))
;; => {:host "127.0.0.1", :port 80}
(def m {:host "127.0.0.1" :port "8008"})
(update m :port (fnil #(Integer/parseInt %) "80"))
;; => {:host "127.0.0.1", :port 8008}
不過我們也可以將其拆開來看,這樣會更好理解
(Integer/parseInt "10") ;; => 10 (Integer/parseInt nil) ;; => Unhandled java.lang.NumberFormatException ((fnil #(Integer/parseInt %) 80) "1000") ;; => 1000
counted?
reversible?
[[ https://clojuredocs.org/clojure.core/reversible_q][reversibl[[file :][]]] 也是 O(1) 的操作,用來查看目標是否有實作 reversible 函式
(reversible? [])
;; => true
(reversible? (sorted-map))
;; => true
(reversible? (sorted-set))
;; => true
(reversible? '())
;; => false
(reversible? {})
;; => false
(reversible? #{})
;; => false
vector-of
vector-of 會根據你提供的類型 (permitive type) 來將參數轉換成相對應型別的向量 (vector)
可以使用的類型有 :int , :long , :float , :double , :byte , :short , :char , :boolean
(conj (vector-of :int) 1 2 3) ;; => [1 2 3] ; <-- note, these are unboxed internally (vector-of :int 1 2 3) ;; => [1 2 3] ; same here (vector-of :float 1 2 3) ;; => [1.0 2.0 3.0] (type (conj (vector-of :int) 1 2 3)) ;; => clojure.core.Vec (type (conj (vector-of :float) 1 2 3)) ;; => clojure.core.Vec
clojure.set/rename-keys
clojure.set/rename-keys 可以用來改變目前現有的 hash-map 的關鍵字 (keyword) 名稱
(require 'clojure.set)
(clojure.set/rename-keys {:a 1, :b 2} {:a :new-a, :b :new-b})
;; => {:new-a 1, :new-b 2}
(cloure.set/rename-keys {:a 1} {:b :new-b})
;; => {:a 1}
;; You need to be careful about key collisions. You probably shouldn't
;; depend on the exact behavior.
(clojure.set/rename-keys {:a 1 :b 2} {:a :b})
;; => {:b 1}
(clojure.set/rename-keys {:a 1 :b 2} {:a :b :b :a})
;; => {:a 1}
clojure.data/diff
clojure.data/diff 用來對兩個序列(sequence)進行比較,並回傳比較的結果
(require 'clojure.data)
(clojure.data/diff {:a 1 :b 2} {:a 3 :b 2 :c 3})
;; => ({:a 1} {:a 3, :c 3} {:b 2})
(clojure.data/diff [1 2 3] [5 9 3 2 3 7])
;; => [[1 2] [5 9 nil 2 3 7] [nil nil 3]]
(clojure.data/diff (set [1 2 3]) (set [5 9 3 2 3 7]))
;; => [#{1} #{7 9 5} #{3 2}]
munge
munge 這個函式並未有文檔,因此只能實際看看用途了
(defn foo [] (println "foo"))
;; => #'user/foo
foo
;; => #<user$foo user$foo@a0dc71>
(munge foo)
;; => "user_DOLLARSIGN_foo_CIRCA_a0dc71"
(doseq [c (remove #(Character/isLetterOrDigit %) (map char (range 32 127)))]
(println c "->" (munge c)))
;; Prints:
;; ->
;; ! -> _BANG_
;; " -> _DOUBLEQUOTE_
;; # -> _SHARP_
;; $ -> $
;; % -> _PERCENT_
;; & -> _AMPERSAND_
;; ' -> _SINGLEQUOTE_
;; ( -> (
;; ) -> )
;; * -> _STAR_
;; + -> _PLUS_
;; , -> ,
;; - -> _
;; . -> .
;; / -> _SLASH_
;; : -> _COLON_
;; ; -> ;
;; < -> _LT_
;; = -> _EQ_
;; > -> _GT_
;; ? -> _QMARK_
;; @ -> _CIRCA_
;; [ -> _LBRACK_
;; \ -> _BSLASH_
;; ] -> _RBRACK_
;; ^ -> _CARET_
;; _ -> _
;; ` -> `
;; { -> _LBRACE_
;; | -> _BAR_
;; } -> _RBRACE_
;; ~ -> _TILDE_
gensym
gensym 會產生不衝突名稱的 symbol,這個應該是在 macro 實現時,Clojure 所使用到的函式
(gensym "foo") ;; => foo2020 (gensym "foo") ;; => foo2027
seque
seque 是一種 阻塞隊列(Linked Blocking Queue, LBQ) 的實作,這種實現是線程安全(thread safe)的,可以確保資料先進先出(first in first out, FIFO) 的狀況。
(let [start (System/nanoTime)
q (seque
(iterate
#(do (Thread/sleep 400) (inc %))
0))]
(println "sleep five seconds...")
(Thread/sleep 5000)
(doseq [i (take 20 q)]
(println (int (/ (- (System/nanoTime) start) 1e7))
":" i)))
;; The iterate form returns a lazy seq that delays nearly a half-second
;; before returning each subsequent item. Here seque starts a thread
;; generating the lazy seq.
;; The body of the let allows the seque thread to get ahead by five seconds
;; before it begins consuming the seq using doseq. The doseq prints a
;; timestamp and the value from the seq when it becomes available. The
;; first 11 or so are available almost instantly, until the consuming
;; doseq catches up with the producing iterate, at which point the consumer
;; blocks for 400ms before each item can be printed.
;;sleep five seconds...
500 : 0
500 : 1
500 : 2
500 : 3
500 : 4
500 : 5
500 : 6
500 : 7
500 : 8
500 : 9
500 : 10
500 : 11
520 : 12
;; ......
clojure.zip/zippers
clojure.zip 是用來處理樹狀結構用函式庫,這篇文章有很好的解釋: (λx.Liu)Blog=Hacking: Understand clojure.zip with picture
clojure.zip/zipper 則是用來將資料包裝起來,這樣我們就可以透過 clojure.zip 來處理這些樹狀資料
(def zp (clojure.zip/zipper vector? seq (fn [_ c] c)
[[1 2 3] [:a :b] 2 3 [40 50 60]]))
;; => [[[1 2 3] [:a :b] 2 3 [40 50 60]] nil]
(clojure.zip/down zp)
;; =>
[[1 2 3]
{:l [],
:pnodes [[[1 2 3] [:a :b] 2 3 [40 50 60]]],
:ppath nil,
:r ([:a :b] 2 3 [40 50 60])}]
(-> zp
clojure.zip/down
first)
;; =>
[1 2 3]
(-> zp
clojure.zip/down
clojure.zip/right)
;; =>
[[:a :b]
{:l [[1 2 3]],
:pnodes [[[1 2 3] [:a :b] 2 3 [40 50 60]]],
:ppath nil,
:r (2 3 [40 50 60])}]
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Unknown perls from the Clojure standard library 筆記
- Unknown perls from the Clojure standard library 筆記
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
MATLAB在数学建模中的应用
卓金武 编 / 北京航空航天大学 / 2011-4 / 34.80元
《MATLAB在数学建模中的应用》从数学建模的角度介绍了MATLAB的应用。《MATLAB在数学建模中的应用》的4位作者均具有实际的数学建模参赛经历和竞赛指导经验。书中内容完全是根据数学建模竞赛的需要而编排的,涵盖了绝大部分数学建模问题的MATLAB求解方法。 《MATLAB在数学建模中的应用》内容分上下两篇。上篇介绍数学建模中常规方法MATLAB的实现,包括MATLAB交互、数据建模、程序......一起来看看 《MATLAB在数学建模中的应用》 这本书的介绍吧!