April 5, 2023
By: Kevin
Clojure的一些小技巧
- #_ 是一个reader special form, 用来跳过下一个表达式, 可以叠在一起用
as->嵌入 -> 来使用- doto 不仅仅是用来和Java做interop的
- 使用
some->来避免null exception - 临时定义个变量
- 使用ex-info来抛出异常
- 自动生成的私有类成员访问Macro
- 使用juxt
- 使用some来提前结束
- set可以当成函数来用, 起过滤作用
- 使用
name函数来规格化string ,symbol,以及keyword - 使用
map-indexed来处理vector - condp + regex
技巧, 算不上最佳实践, 用到时候会为自己的的聪明开心一下.
#_ 是一个reader special form, 用来跳过下一个表达式, 可以叠在一起用
(let [#_#_
a 1
b 2]
[b])
;;相当于
(let [#_a #_1
b 2]
[b])
值得说明一下的是 #_并不是注释. (comment ...) 求值的结果是nil, 而 #_ 意思是跳过下个表达式.
as-> 嵌入 -> 来使用
(-> [10 11]
(conj 12)
(as-> xs (map - xs [3 2 1]))
(reverse))
;; 相当于
(-> [10 11]
(conj 12)
(#(map - % [3 2 1]))
(reverse))
doto 不仅仅是用来和Java做interop的
(doto 'my.test-ns require in-ns clojure.test/run-tests)
使用 some-> 来避免null exception
现代语言中, C#, swift都有?操作符(safe navigation operator), some->可以起到同样的效果.
(some-> pane .getParent .getChildren (.remove pane)))
some->>当然也是一样的.
临时定义个变量
(defn awesome [x]
(def debug-last-input x)
; your decomplected goodness here
(throw (Exception. "some crazy bug that is hard to reproduce"))
clojure1.10以后, tap>是个更好的选项.
使用ex-info来抛出异常
(throw (ex-info "some crazy bug that is hard to reproduce" {}))
ex-info 在clj/cljs均可用
自动生成的私有类成员访问Macro
(defmacro expose-private-accessors
"Given a java class, `klass`, and a seq of symbols defining assumably private
fields in said class, returns a map of keyworded getters and setters, that
associate with functions that operate on said object to access the fields.
Yields public function definitions of the form set-fieldname, get-fieldname
for each field."
[klass & fields]
(let [m (with-meta (gensym "m" ) {:tag 'java.lang.reflect.Field})
o (with-meta (gensym "o") {:tag klass})
fld-get-sets (vec (for [f fields]
[(str f)
(symbol (str "get-" f))
(symbol (str "set-" f))]))]
`(do ~@
(for [[fld getter setter] fld-get-sets]
`(let [~m (.getDeclaredField ~klass ~fld)
~'_ (.setAccessible ~m true)]
(defn ~getter [~o] (.get ~m ~o))
(defn ~setter [~o v#] (do (.set ~m ~o v#) ~o)))))))
使用juxt
(def separate (juxt filter remove))
(separate pos? [1 2 3 -1 0 4 -2])
;; => [(1 2 3 4) (-1 0 -2)]
使用some来提前结束
找到第一个满足条件的就返回
(some #(when (zero? (rem % 7)) %) (range 50 10000000000000000000))
;; => 56
keep则起到过滤的作用, 会处理输入序列中的每一个值
(keep #(when (zero? (rem % 7)) (str "value: " %)) (range 50 100))
;; => ("value: 56" "value: 63" "value: 70" "value: 77" "value: 84" "value: 91" "value: 98")
set可以当成函数来用, 起过滤作用
(filter (comp #{"one" "three"} :id)
[{:id "one"}
{:id "two"}
{:id "three"}
{:id "four"}])
=> ({:id "one"} {:id "three"})
使用name函数来规格化string ,symbol,以及keyword
=>(map (comp keyword name) [:hello 'world "!"])
(:hello :world :!)
使用map-indexed来处理vector
(for [[idx v] (map-indexed vector [:some :elements :of :seq])]
{:idx idx :v v})
;; => ({:idx 0, :v :some} {:idx 1, :v :elements} {:idx 2, :v :of} {:idx 3, :v :seq})
(->> [:some :elements :of :seq]
(map-indexed vector)
(into {}))
;; => {0 :some, 1 :elements, 2 :of, 3 :seq}
condp + regex
(let [input "Alexa, set a timer for 10 seconds"]
(condp re-matches input
#"Alexa, turn on (.*)" :>> (fn [[_ thing]] [:turn-on thing])
#"Alexa, turn off (.*)" :>> (fn [[_ thing]] [:turn-off thing])
#"Alexa, set a timer for (.*) (minutes|seconds)"
:>> (fn [[_ amount units]] [:start-timer amount units])
#"Alexa, what's the weather like (.*)"
:>> (fn [[_ when]]
(case when
"today" [:weather-today]
"tomorrow" [:weather-tomorrow]))))
;; => [:start-timer "10" "seconds"]