为什么绑定会影响我的地图类型?

Dao*_*Wen 16 language-implementation clojure map

我在REPL中玩耍,我有一些奇怪的行为:

Clojure 1.4.0
user=> (type {:a 1})
clojure.lang.PersistentArrayMap
user=> (def x {:a 1})
#'user/x
user=> (type x)
clojure.lang.PersistentHashMap
Run Code Online (Sandbox Code Playgroud)

我认为所有小的文字地图都是实例PersistentArrayMap,但显然情况并非如此def.为什么要使用defClojure为我的litte地图选择不同的表示?我知道这可能只是一些奇怪的实现细节,但我很好奇.

Ale*_*min 15

这个问题让我深入研究了Clojure的源代码.我只花了几个小时将打印语句放在源代码中以便弄清楚这一点.

事实证明,通过不同的代码路径评估两个映射表达式

(type {:a 1})导致Java字节码被发出并运行.发出的代码用于clojure.lang.RT.map()构造地图,该地图返回小地图的PersistentArrayMap:

static public IPersistentMap map(Object... init){
    if(init == null)
        return PersistentArrayMap.EMPTY;
    else if(init.length <= PersistentArrayMap.HASHTABLE_THRESHOLD)
        return PersistentArrayMap.createWithCheck(init);
    return PersistentHashMap.createWithCheck(init);
}
Run Code Online (Sandbox Code Playgroud)

(def x {:a 1})至少从REPL进行评估时,没有发出字节码.常量映射被解析为PersistentHashMap,clojure.lang.Compiler$MapExpr.parse()其中返回它将其变形为ConstantExpr:

else if(constant)
{
IPersistentMap m = PersistentHashMap.EMPTY;
for(int i=0;i<keyvals.length();i+= 2)
    {
    m = m.assoc(((LiteralExpr)keyvals.nth(i)).val(), ((LiteralExpr)keyvals.nth(i+1)).val());
    }
//System.err.println("Constant: " + m);
return new ConstantExpr(m);
}
Run Code Online (Sandbox Code Playgroud)

def评估时的表达式绑定ConstantExpr上面创建的值,如上所述是PersistentHashMap.

那么为什么这样实现呢?

我不知道.这可能是简单的疏忽,或者PersistentArrayMap优化可能并不值得.

  • 非常好的解释!根据你的回答,编译.clj并在REPL之外运行它应该导致两个值都是`PersistentArrayMap`类型.我添加了一个主方法来打印这两种类型,uberjar将它与`java -jar`一起运行并且确定它们都是数组映射! (3认同)