庄周梦蝶

生活、程序、未来

声明:本博客所有文章,未经允许,禁止转载。谢谢。

工作所得之一二

| Comments

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)

声明:本博客所有文章,未经允许,禁止转载。谢谢。

Uncategorized, 开发心得

« 节奏 离开帝都 »