Clojure transient集合“陷阱”
掉进了一个坑两次,希望不会有第三次,下面这个代码,你认为最终的x会有几组元素?
(let [x (transient {})] (dotimes [n 30] (assoc! x n n)) (count x))
30个?很遗憾,你跟我犯了同样的错误。Transient集合虽然是mutable的,但是要注意调用assoc!或者dissoc!等修改操作的时候仍然需要“收集并使用”返回值。也就是上面代码需要修改为:
(reduce #(assoc! %1 %2 %2) (transient {}) (range 0 30))
这样制造出来的集合才是30个元素,符合你的预期。
如果确实需要收集(比如跨越多个函数做收集,虽然是很坏的实践,为什么?),可以直接使用java的HashMap,我们可以编写几个辅助函数:
(defn jhash-map [ & opts] (doto (java.util.HashMap.) (.putAll (apply hash-map opts)))) (defn put! [^java.util.Map m k v] (.put m k v)) (defn remove! [^java.util.Map m k] (.remove m k)) (defn immutable! [m] (into {} m))
使用:
user=> (jhash-map :a 1)
{:a 1}
user=> (jhash-map :a 1 :b 2)
{:b 2, :a 1}
user=> (def m (jhash-map))
# 'user
user=> (dotimes [n 30] (put! m n n))
nil
user=> (get m 29)
29
user=> (remove! m 29)
29
user=> (count m)
29
user=> (immutable! m)
{0 0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7, 8 8, 9 9, 10 10, 11 11, 12 12, 13 13, 14 14, 15 15, 16 16, 17 17, 18 18, 19 19, 20 20, 21 21, 22 22, 23 23, 24 24, 25 25, 26 26, 27 27, 28 28}
文件中的BOM字符
Unicode字符集有个BOM来标示字节序,所谓Byte Order Mark,具体可以看维基百科上的介绍。Windows记事本在保存为UTF-8格式的时候,会在文件开头插入这个字节序标示符:EF BB BF
。Java在读取这样的文件的时候,会将这些不可见字符读出来,假如是一个格式化的文件(JSON或者XML之类),那么在解析的时候会造成不必要的麻烦。因此需要自动检测这样的文件并消除BOM字符。
简单的做法是使用commons-io的BOMInputStream:
(ns test (:import [org.apache.commons.io.input BOMInputStream] [org.apache.commons.io ByteOrderMark])) (defn slurp-file [f] (slurp (BOMInputStream. f false (into-array ByteOrderMark [ByteOrderMark/UTF_8, ByteOrderMark/UTF_16LE, ByteOrderMark/UTF_16BE, ByteOrderMark/UTF_32LE, ByteOrderMark/UTF_32BE])) :encoding "utf-8"))
上面的代码也示范了怎么在clojure里调用Java的可变参数方法,使用into-array
将可变参数组成数组传入即可。
CoffeeScript的==和!=
这个问题主要是对coffeescript没有那么熟悉,虽然也写了两千行的代码了,最近还是不小心掉进了坑。CoffeeScript的==和!=,以及is都是编译成JavaScript的===和!===,也就是所谓Identity比较,它希望两端比较的东西是完全一致的,不需要转型之类:
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
上面示范来自万能的stackoverflow。
我犯的错误就是习惯性的用==来判断null或者undefined:
(x) -> y = if x != null 1 else 2
其实应该用x?来判断:
(x) -> y = if x? 1 else 2
x?会编译成:(typeof x !== "undefined" && x !== null)