好吧,我对 identical?
的认知不准确。它调用了 clojure.lang.Util/identical
,其实现如下
static public boolean identical(Object k1, Object k2){
return k1 == k2;
}
当 Clojure 调用一个具有此签名的函数时,它必须将参数中的原始值装箱。Double 通过调用 Double.valueOf(double)
来装箱。NaN 的不同等价语义使示例变得难以理解,但在每种情况下,identical?
都比较装箱值的引用等价性,而不是数值等价性。
通过查看反汇编的 bytecode(见下文),我们可以看到发生了什么。以下是摘要。
- 在示例 1、2 和 4 中,每个
identical?
的参数都是单独装箱的。
- 在示例3中,
##NaN
只被装箱一次,并且相同的装箱值被用作 identical?
的两个参数。
下面是更多示例。请注意,##Inf
的行为与 ##NaN
一致。
(identical? ##Inf ##Inf) ; true
(let [x ##Inf] (identical? x x)) ; false
(identical? 1.0 1.0) ; false
(let [x 1.0] (identical? x x)) ; false
(def my-one 1.0)
(identical? my-one my-one) ; true
结论
一切如预期运行。我对 identical?
的理解是错误的。它更像是 (Object)... == (Object) ...
而非简单的 ==
。
学习心得:仅用 identical?
进行引用等价性比较。
反汇编示例
代码已使用 clj-java-decompiler 进行反汇编。
示例1 (identical? Double/NaN Double/NaN)
class user$fn_line_1__254
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: getstatic java/lang/Double.NaN:D
3: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
6: getstatic java/lang/Double.NaN:D
9: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
12: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq 24
18: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto 27
24: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 18 42 07 00 1D
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__254.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: return
示例2 (let [x Double/NaN] (identical? x x))
跳过;与示例4类似。
示例3 (identical? ##NaN ##NaN)
请注意,装箱发生在静态块中。
class user$fn_line_1__246
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public static final java.lang.Object const__1;
Flags: PUBLIC, STATIC, FINAL
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: getstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
3: getstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
linenumber 1
6: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
9: ifeq 18
12: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
15: goto 21
18: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
21: areturn
StackMapTable: 00 02 12 42 07 00 17
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__246.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: ldc2_w NaN
3: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
6: putstatic user$fn_line_1__246.const__1:Ljava/lang/Object;
9: return
示例4 (let [x ##NaN] (identical? x x))
在 invokeStatic 方法中有两个 Double.valueOf
调用。
class user$fn_line_1__250
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER
public void <init>();
Flags: PUBLIC
Code:
linenumber 1
0: aload_0
1: invokespecial clojure/lang/AFunction.<init>:()V
4: return
public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: ldc2_w NaN
3: dstore_0 /* x */
4: dload_0 /* x */
5: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
8: dload_0 /* x */
9: invokestatic java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber 1
12: invokestatic clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq 24
18: getstatic java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto 27
24: getstatic java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 FC 00 18 03 42 07 00 1B
public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber 1
0: invokestatic user$fn_line_1__250.invokeStatic:()Ljava/lang/Object;
3: areturn
static {};
Flags: PUBLIC, STATIC
Code:
linenumber 1
0: return